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.List; 021import java.util.concurrent.ExecutorService; 022import java.util.concurrent.ScheduledExecutorService; 023import javax.xml.bind.annotation.XmlAccessType; 024import javax.xml.bind.annotation.XmlAccessorType; 025import javax.xml.bind.annotation.XmlAttribute; 026import javax.xml.bind.annotation.XmlElement; 027import javax.xml.bind.annotation.XmlElementRef; 028import javax.xml.bind.annotation.XmlRootElement; 029import javax.xml.bind.annotation.XmlTransient; 030 031import org.apache.camel.CamelContextAware; 032import org.apache.camel.Expression; 033import org.apache.camel.Predicate; 034import org.apache.camel.Processor; 035import org.apache.camel.builder.AggregationStrategyClause; 036import org.apache.camel.builder.ExpressionClause; 037import org.apache.camel.builder.PredicateClause; 038import org.apache.camel.model.language.ExpressionDefinition; 039import org.apache.camel.processor.CamelInternalProcessor; 040import org.apache.camel.processor.aggregate.AggregateController; 041import org.apache.camel.processor.aggregate.AggregateProcessor; 042import org.apache.camel.processor.aggregate.AggregationStrategy; 043import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter; 044import org.apache.camel.processor.aggregate.ClosedCorrelationKeyException; 045import org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy; 046import org.apache.camel.processor.aggregate.OptimisticLockRetryPolicy; 047import org.apache.camel.spi.AggregationRepository; 048import org.apache.camel.spi.AsPredicate; 049import org.apache.camel.spi.Metadata; 050import org.apache.camel.spi.RouteContext; 051import org.apache.camel.util.concurrent.SynchronousExecutorService; 052 053/** 054 * Aggregates many messages into a single message 055 * 056 * @version 057 */ 058@Metadata(label = "eip,routing") 059@XmlRootElement(name = "aggregate") 060@XmlAccessorType(XmlAccessType.FIELD) 061public class AggregateDefinition extends ProcessorDefinition<AggregateDefinition> implements ExecutorServiceAwareDefinition<AggregateDefinition> { 062 @XmlElement(name = "correlationExpression", required = true) 063 private ExpressionSubElementDefinition correlationExpression; 064 @XmlElement(name = "completionPredicate") @AsPredicate 065 private ExpressionSubElementDefinition completionPredicate; 066 @XmlElement(name = "completionTimeout") 067 private ExpressionSubElementDefinition completionTimeoutExpression; 068 @XmlElement(name = "completionSize") 069 private ExpressionSubElementDefinition completionSizeExpression; 070 @XmlElement(name = "optimisticLockRetryPolicy") 071 private OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition; 072 @XmlTransient 073 private ExpressionDefinition expression; 074 @XmlTransient 075 private AggregationStrategy aggregationStrategy; 076 @XmlTransient 077 private ExecutorService executorService; 078 @XmlTransient 079 private ScheduledExecutorService timeoutCheckerExecutorService; 080 @XmlTransient 081 private AggregationRepository aggregationRepository; 082 @XmlTransient 083 private OptimisticLockRetryPolicy optimisticLockRetryPolicy; 084 @XmlAttribute 085 private Boolean parallelProcessing; 086 @XmlAttribute 087 private Boolean optimisticLocking; 088 @XmlAttribute 089 private String executorServiceRef; 090 @XmlAttribute 091 private String timeoutCheckerExecutorServiceRef; 092 @XmlAttribute 093 private String aggregationRepositoryRef; 094 @XmlAttribute 095 private String strategyRef; 096 @XmlAttribute 097 private String strategyMethodName; 098 @XmlAttribute 099 private Boolean strategyMethodAllowNull; 100 @XmlAttribute 101 private Integer completionSize; 102 @XmlAttribute 103 private Long completionInterval; 104 @XmlAttribute 105 private Long completionTimeout; 106 @XmlAttribute 107 private Boolean completionFromBatchConsumer; 108 @XmlAttribute 109 @Deprecated 110 private Boolean groupExchanges; 111 @XmlAttribute 112 private Boolean eagerCheckCompletion; 113 @XmlAttribute 114 private Boolean ignoreInvalidCorrelationKeys; 115 @XmlAttribute 116 private Integer closeCorrelationKeyOnCompletion; 117 @XmlAttribute 118 private Boolean discardOnCompletionTimeout; 119 @XmlAttribute 120 private Boolean forceCompletionOnStop; 121 @XmlAttribute 122 private Boolean completeAllOnStop; 123 @XmlTransient 124 private AggregateController aggregateController; 125 @XmlAttribute 126 private String aggregateControllerRef; 127 @XmlElementRef 128 private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>(); 129 130 public AggregateDefinition() { 131 } 132 133 public AggregateDefinition(@AsPredicate Predicate predicate) { 134 this(ExpressionNodeHelper.toExpressionDefinition(predicate)); 135 } 136 137 public AggregateDefinition(Expression expression) { 138 this(ExpressionNodeHelper.toExpressionDefinition(expression)); 139 } 140 141 public AggregateDefinition(ExpressionDefinition correlationExpression) { 142 setExpression(correlationExpression); 143 144 ExpressionSubElementDefinition cor = new ExpressionSubElementDefinition(); 145 cor.setExpressionType(correlationExpression); 146 setCorrelationExpression(cor); 147 } 148 149 public AggregateDefinition(Expression correlationExpression, AggregationStrategy aggregationStrategy) { 150 this(correlationExpression); 151 this.aggregationStrategy = aggregationStrategy; 152 } 153 154 @Override 155 public String toString() { 156 return "Aggregate[" + description() + " -> " + getOutputs() + "]"; 157 } 158 159 protected String description() { 160 return getExpression() != null ? getExpression().getLabel() : ""; 161 } 162 163 @Override 164 public String getLabel() { 165 return "aggregate[" + description() + "]"; 166 } 167 168 @Override 169 public Processor createProcessor(RouteContext routeContext) throws Exception { 170 return createAggregator(routeContext); 171 } 172 173 protected AggregateProcessor createAggregator(RouteContext routeContext) throws Exception { 174 Processor childProcessor = this.createChildProcessor(routeContext, true); 175 176 // wrap the aggregate route in a unit of work processor 177 CamelInternalProcessor internal = new CamelInternalProcessor(childProcessor); 178 internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(routeContext)); 179 180 Expression correlation = getExpression().createExpression(routeContext); 181 AggregationStrategy strategy = createAggregationStrategy(routeContext); 182 183 boolean parallel = getParallelProcessing() != null && getParallelProcessing(); 184 boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, parallel); 185 ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "Aggregator", this, parallel); 186 if (threadPool == null && !parallel) { 187 // executor service is mandatory for the Aggregator 188 // we do not run in parallel mode, but use a synchronous executor, so we run in current thread 189 threadPool = new SynchronousExecutorService(); 190 shutdownThreadPool = true; 191 } 192 193 AggregateProcessor answer = new AggregateProcessor(routeContext.getCamelContext(), internal, 194 correlation, strategy, threadPool, shutdownThreadPool); 195 196 AggregationRepository repository = createAggregationRepository(routeContext); 197 if (repository != null) { 198 answer.setAggregationRepository(repository); 199 } 200 201 if (getAggregateController() == null && getAggregateControllerRef() != null) { 202 setAggregateController(routeContext.mandatoryLookup(getAggregateControllerRef(), AggregateController.class)); 203 } 204 205 // this EIP supports using a shared timeout checker thread pool or fallback to create a new thread pool 206 boolean shutdownTimeoutThreadPool = false; 207 ScheduledExecutorService timeoutThreadPool = timeoutCheckerExecutorService; 208 if (timeoutThreadPool == null && timeoutCheckerExecutorServiceRef != null) { 209 // lookup existing thread pool 210 timeoutThreadPool = routeContext.getCamelContext().getRegistry().lookupByNameAndType(timeoutCheckerExecutorServiceRef, ScheduledExecutorService.class); 211 if (timeoutThreadPool == null) { 212 // then create a thread pool assuming the ref is a thread pool profile id 213 timeoutThreadPool = routeContext.getCamelContext().getExecutorServiceManager().newScheduledThreadPool(this, 214 AggregateProcessor.AGGREGATE_TIMEOUT_CHECKER, timeoutCheckerExecutorServiceRef); 215 if (timeoutThreadPool == null) { 216 throw new IllegalArgumentException("ExecutorServiceRef " + timeoutCheckerExecutorServiceRef 217 + " not found in registry (as an ScheduledExecutorService instance) or as a thread pool profile."); 218 } 219 shutdownTimeoutThreadPool = true; 220 } 221 } 222 answer.setTimeoutCheckerExecutorService(timeoutThreadPool); 223 answer.setShutdownTimeoutCheckerExecutorService(shutdownTimeoutThreadPool); 224 225 // set other options 226 answer.setParallelProcessing(parallel); 227 if (getOptimisticLocking() != null) { 228 answer.setOptimisticLocking(getOptimisticLocking()); 229 } 230 if (getCompletionPredicate() != null) { 231 Predicate predicate = getCompletionPredicate().createPredicate(routeContext); 232 answer.setCompletionPredicate(predicate); 233 } else if (strategy instanceof Predicate) { 234 // if aggregation strategy implements predicate and was not configured then use as fallback 235 log.debug("Using AggregationStrategy as completion predicate: {}", strategy); 236 answer.setCompletionPredicate((Predicate) strategy); 237 } 238 if (getCompletionTimeoutExpression() != null) { 239 Expression expression = getCompletionTimeoutExpression().createExpression(routeContext); 240 answer.setCompletionTimeoutExpression(expression); 241 } 242 if (getCompletionTimeout() != null) { 243 answer.setCompletionTimeout(getCompletionTimeout()); 244 } 245 if (getCompletionInterval() != null) { 246 answer.setCompletionInterval(getCompletionInterval()); 247 } 248 if (getCompletionSizeExpression() != null) { 249 Expression expression = getCompletionSizeExpression().createExpression(routeContext); 250 answer.setCompletionSizeExpression(expression); 251 } 252 if (getCompletionSize() != null) { 253 answer.setCompletionSize(getCompletionSize()); 254 } 255 if (getCompletionFromBatchConsumer() != null) { 256 answer.setCompletionFromBatchConsumer(getCompletionFromBatchConsumer()); 257 } 258 if (getEagerCheckCompletion() != null) { 259 answer.setEagerCheckCompletion(getEagerCheckCompletion()); 260 } 261 if (getIgnoreInvalidCorrelationKeys() != null) { 262 answer.setIgnoreInvalidCorrelationKeys(getIgnoreInvalidCorrelationKeys()); 263 } 264 if (getCloseCorrelationKeyOnCompletion() != null) { 265 answer.setCloseCorrelationKeyOnCompletion(getCloseCorrelationKeyOnCompletion()); 266 } 267 if (getDiscardOnCompletionTimeout() != null) { 268 answer.setDiscardOnCompletionTimeout(getDiscardOnCompletionTimeout()); 269 } 270 if (getForceCompletionOnStop() != null) { 271 answer.setForceCompletionOnStop(getForceCompletionOnStop()); 272 } 273 if (getCompleteAllOnStop() != null) { 274 answer.setCompleteAllOnStop(getCompleteAllOnStop()); 275 } 276 if (optimisticLockRetryPolicy == null) { 277 if (getOptimisticLockRetryPolicyDefinition() != null) { 278 answer.setOptimisticLockRetryPolicy(getOptimisticLockRetryPolicyDefinition().createOptimisticLockRetryPolicy()); 279 } 280 } else { 281 answer.setOptimisticLockRetryPolicy(optimisticLockRetryPolicy); 282 } 283 if (getAggregateController() != null) { 284 answer.setAggregateController(getAggregateController()); 285 } 286 return answer; 287 } 288 289 @Override 290 public void configureChild(ProcessorDefinition<?> output) { 291 if (expression != null && expression instanceof ExpressionClause) { 292 ExpressionClause<?> clause = (ExpressionClause<?>) expression; 293 if (clause.getExpressionType() != null) { 294 // if using the Java DSL then the expression may have been set using the 295 // ExpressionClause which is a fancy builder to define expressions and predicates 296 // using fluent builders in the DSL. However we need afterwards a callback to 297 // reset the expression to the expression type the ExpressionClause did build for us 298 expression = clause.getExpressionType(); 299 // set the correlation expression from the expression type, as the model definition 300 // would then be accurate 301 correlationExpression = new ExpressionSubElementDefinition(); 302 correlationExpression.setExpressionType(clause.getExpressionType()); 303 } 304 } 305 } 306 307 private AggregationStrategy createAggregationStrategy(RouteContext routeContext) { 308 AggregationStrategy strategy = getAggregationStrategy(); 309 if (strategy == null && strategyRef != null) { 310 Object aggStrategy = routeContext.lookup(strategyRef, Object.class); 311 if (aggStrategy instanceof AggregationStrategy) { 312 strategy = (AggregationStrategy) aggStrategy; 313 } else if (aggStrategy != null) { 314 AggregationStrategyBeanAdapter adapter = new AggregationStrategyBeanAdapter(aggStrategy, getAggregationStrategyMethodName()); 315 if (getStrategyMethodAllowNull() != null) { 316 adapter.setAllowNullNewExchange(getStrategyMethodAllowNull()); 317 adapter.setAllowNullOldExchange(getStrategyMethodAllowNull()); 318 } 319 strategy = adapter; 320 } else { 321 throw new IllegalArgumentException("Cannot find AggregationStrategy in Registry with name: " + strategyRef); 322 } 323 } 324 325 if (groupExchanges != null && groupExchanges) { 326 if (strategy != null || strategyRef != null) { 327 throw new IllegalArgumentException("Options groupExchanges and AggregationStrategy cannot be enabled at the same time"); 328 } 329 if (eagerCheckCompletion != null && !eagerCheckCompletion) { 330 throw new IllegalArgumentException("Option eagerCheckCompletion cannot be false when groupExchanges has been enabled"); 331 } 332 // set eager check to enabled by default when using grouped exchanges 333 setEagerCheckCompletion(true); 334 // if grouped exchange is enabled then use special strategy for that 335 strategy = new GroupedExchangeAggregationStrategy(); 336 } 337 338 if (strategy == null) { 339 throw new IllegalArgumentException("AggregationStrategy or AggregationStrategyRef must be set on " + this); 340 } 341 342 if (strategy instanceof CamelContextAware) { 343 ((CamelContextAware) strategy).setCamelContext(routeContext.getCamelContext()); 344 } 345 346 return strategy; 347 } 348 349 private AggregationRepository createAggregationRepository(RouteContext routeContext) { 350 AggregationRepository repository = getAggregationRepository(); 351 if (repository == null && aggregationRepositoryRef != null) { 352 repository = routeContext.mandatoryLookup(aggregationRepositoryRef, AggregationRepository.class); 353 } 354 return repository; 355 } 356 357 public AggregationStrategy getAggregationStrategy() { 358 return aggregationStrategy; 359 } 360 361 /** 362 * The AggregationStrategy to use. 363 * <p/> 364 * Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges. 365 * At first call the oldExchange parameter is null. 366 * On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange. 367 */ 368 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) { 369 this.aggregationStrategy = aggregationStrategy; 370 } 371 372 public String getAggregationStrategyRef() { 373 return strategyRef; 374 } 375 376 /** 377 * A reference to lookup the AggregationStrategy in the Registry. 378 * <p/> 379 * Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges. 380 * At first call the oldExchange parameter is null. 381 * On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange. 382 */ 383 public void setAggregationStrategyRef(String aggregationStrategyRef) { 384 this.strategyRef = aggregationStrategyRef; 385 } 386 387 public String getStrategyRef() { 388 return strategyRef; 389 } 390 391 /** 392 * A reference to lookup the AggregationStrategy in the Registry. 393 * <p/> 394 * Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges. 395 * At first call the oldExchange parameter is null. 396 * On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange. 397 */ 398 public void setStrategyRef(String strategyRef) { 399 this.strategyRef = strategyRef; 400 } 401 402 public String getAggregationStrategyMethodName() { 403 return strategyMethodName; 404 } 405 406 /** 407 * This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy. 408 */ 409 public void setAggregationStrategyMethodName(String strategyMethodName) { 410 this.strategyMethodName = strategyMethodName; 411 } 412 413 public Boolean getStrategyMethodAllowNull() { 414 return strategyMethodAllowNull; 415 } 416 417 public String getStrategyMethodName() { 418 return strategyMethodName; 419 } 420 421 /** 422 * This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy. 423 */ 424 public void setStrategyMethodName(String strategyMethodName) { 425 this.strategyMethodName = strategyMethodName; 426 } 427 428 /** 429 * If this option is false then the aggregate method is not used for the very first aggregation. 430 * If this option is true then null values is used as the oldExchange (at the very first aggregation), 431 * when using POJOs as the AggregationStrategy. 432 */ 433 public void setStrategyMethodAllowNull(Boolean strategyMethodAllowNull) { 434 this.strategyMethodAllowNull = strategyMethodAllowNull; 435 } 436 437 /** 438 * The expression used to calculate the correlation key to use for aggregation. 439 * The Exchange which has the same correlation key is aggregated together. 440 * If the correlation key could not be evaluated an Exception is thrown. 441 * You can disable this by using the ignoreBadCorrelationKeys option. 442 */ 443 public void setCorrelationExpression(ExpressionSubElementDefinition correlationExpression) { 444 this.correlationExpression = correlationExpression; 445 } 446 447 public ExpressionSubElementDefinition getCorrelationExpression() { 448 return correlationExpression; 449 } 450 451 public Integer getCompletionSize() { 452 return completionSize; 453 } 454 455 public void setCompletionSize(Integer completionSize) { 456 this.completionSize = completionSize; 457 } 458 459 public OptimisticLockRetryPolicyDefinition getOptimisticLockRetryPolicyDefinition() { 460 return optimisticLockRetryPolicyDefinition; 461 } 462 463 public void setOptimisticLockRetryPolicyDefinition(OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition) { 464 this.optimisticLockRetryPolicyDefinition = optimisticLockRetryPolicyDefinition; 465 } 466 467 public OptimisticLockRetryPolicy getOptimisticLockRetryPolicy() { 468 return optimisticLockRetryPolicy; 469 } 470 471 public void setOptimisticLockRetryPolicy(OptimisticLockRetryPolicy optimisticLockRetryPolicy) { 472 this.optimisticLockRetryPolicy = optimisticLockRetryPolicy; 473 } 474 475 public Long getCompletionInterval() { 476 return completionInterval; 477 } 478 479 public void setCompletionInterval(Long completionInterval) { 480 this.completionInterval = completionInterval; 481 } 482 483 public Long getCompletionTimeout() { 484 return completionTimeout; 485 } 486 487 public void setCompletionTimeout(Long completionTimeout) { 488 this.completionTimeout = completionTimeout; 489 } 490 491 public ExpressionSubElementDefinition getCompletionPredicate() { 492 return completionPredicate; 493 } 494 495 public void setCompletionPredicate(ExpressionSubElementDefinition completionPredicate) { 496 this.completionPredicate = completionPredicate; 497 } 498 499 public ExpressionSubElementDefinition getCompletionTimeoutExpression() { 500 return completionTimeoutExpression; 501 } 502 503 public void setCompletionTimeoutExpression(ExpressionSubElementDefinition completionTimeoutExpression) { 504 this.completionTimeoutExpression = completionTimeoutExpression; 505 } 506 507 public ExpressionSubElementDefinition getCompletionSizeExpression() { 508 return completionSizeExpression; 509 } 510 511 public void setCompletionSizeExpression(ExpressionSubElementDefinition completionSizeExpression) { 512 this.completionSizeExpression = completionSizeExpression; 513 } 514 515 public Boolean getGroupExchanges() { 516 return groupExchanges; 517 } 518 519 public void setGroupExchanges(Boolean groupExchanges) { 520 this.groupExchanges = groupExchanges; 521 } 522 523 public Boolean getCompletionFromBatchConsumer() { 524 return completionFromBatchConsumer; 525 } 526 527 public void setCompletionFromBatchConsumer(Boolean completionFromBatchConsumer) { 528 this.completionFromBatchConsumer = completionFromBatchConsumer; 529 } 530 531 public ExecutorService getExecutorService() { 532 return executorService; 533 } 534 535 public void setExecutorService(ExecutorService executorService) { 536 this.executorService = executorService; 537 } 538 539 public Boolean getOptimisticLocking() { 540 return optimisticLocking; 541 } 542 543 public void setOptimisticLocking(boolean optimisticLocking) { 544 this.optimisticLocking = optimisticLocking; 545 } 546 547 public Boolean getParallelProcessing() { 548 return parallelProcessing; 549 } 550 551 public void setParallelProcessing(boolean parallelProcessing) { 552 this.parallelProcessing = parallelProcessing; 553 } 554 555 public String getExecutorServiceRef() { 556 return executorServiceRef; 557 } 558 559 public void setExecutorServiceRef(String executorServiceRef) { 560 this.executorServiceRef = executorServiceRef; 561 } 562 563 public Boolean getEagerCheckCompletion() { 564 return eagerCheckCompletion; 565 } 566 567 public void setEagerCheckCompletion(Boolean eagerCheckCompletion) { 568 this.eagerCheckCompletion = eagerCheckCompletion; 569 } 570 571 public Boolean getIgnoreInvalidCorrelationKeys() { 572 return ignoreInvalidCorrelationKeys; 573 } 574 575 public void setIgnoreInvalidCorrelationKeys(Boolean ignoreInvalidCorrelationKeys) { 576 this.ignoreInvalidCorrelationKeys = ignoreInvalidCorrelationKeys; 577 } 578 579 public Integer getCloseCorrelationKeyOnCompletion() { 580 return closeCorrelationKeyOnCompletion; 581 } 582 583 public void setCloseCorrelationKeyOnCompletion(Integer closeCorrelationKeyOnCompletion) { 584 this.closeCorrelationKeyOnCompletion = closeCorrelationKeyOnCompletion; 585 } 586 587 public AggregationRepository getAggregationRepository() { 588 return aggregationRepository; 589 } 590 591 public void setAggregationRepository(AggregationRepository aggregationRepository) { 592 this.aggregationRepository = aggregationRepository; 593 } 594 595 public String getAggregationRepositoryRef() { 596 return aggregationRepositoryRef; 597 } 598 599 public void setAggregationRepositoryRef(String aggregationRepositoryRef) { 600 this.aggregationRepositoryRef = aggregationRepositoryRef; 601 } 602 603 public Boolean getDiscardOnCompletionTimeout() { 604 return discardOnCompletionTimeout; 605 } 606 607 public void setDiscardOnCompletionTimeout(Boolean discardOnCompletionTimeout) { 608 this.discardOnCompletionTimeout = discardOnCompletionTimeout; 609 } 610 611 public void setTimeoutCheckerExecutorService(ScheduledExecutorService timeoutCheckerExecutorService) { 612 this.timeoutCheckerExecutorService = timeoutCheckerExecutorService; 613 } 614 615 public ScheduledExecutorService getTimeoutCheckerExecutorService() { 616 return timeoutCheckerExecutorService; 617 } 618 619 public void setTimeoutCheckerExecutorServiceRef(String timeoutCheckerExecutorServiceRef) { 620 this.timeoutCheckerExecutorServiceRef = timeoutCheckerExecutorServiceRef; 621 } 622 623 public String getTimeoutCheckerExecutorServiceRef() { 624 return timeoutCheckerExecutorServiceRef; 625 } 626 627 public Boolean getForceCompletionOnStop() { 628 return forceCompletionOnStop; 629 } 630 631 public void setForceCompletionOnStop(Boolean forceCompletionOnStop) { 632 this.forceCompletionOnStop = forceCompletionOnStop; 633 } 634 635 public Boolean getCompleteAllOnStop() { 636 return completeAllOnStop; 637 } 638 639 public void setCompleteAllOnStop(Boolean completeAllOnStop) { 640 this.completeAllOnStop = completeAllOnStop; 641 } 642 643 public AggregateController getAggregateController() { 644 return aggregateController; 645 } 646 647 public void setAggregateController(AggregateController aggregateController) { 648 this.aggregateController = aggregateController; 649 } 650 651 public String getAggregateControllerRef() { 652 return aggregateControllerRef; 653 } 654 655 /** 656 * To use a {@link org.apache.camel.processor.aggregate.AggregateController} to allow external sources to control 657 * this aggregator. 658 */ 659 public void setAggregateControllerRef(String aggregateControllerRef) { 660 this.aggregateControllerRef = aggregateControllerRef; 661 } 662 663 // Fluent API 664 //------------------------------------------------------------------------- 665 666 /** 667 * Use eager completion checking which means that the {{completionPredicate}} will use the incoming Exchange. 668 * As opposed to without eager completion checking the {{completionPredicate}} will use the aggregated Exchange. 669 * 670 * @return builder 671 */ 672 public AggregateDefinition eagerCheckCompletion() { 673 setEagerCheckCompletion(true); 674 return this; 675 } 676 677 /** 678 * If a correlation key cannot be successfully evaluated it will be ignored by logging a {{DEBUG}} and then just 679 * ignore the incoming Exchange. 680 * 681 * @return builder 682 */ 683 public AggregateDefinition ignoreInvalidCorrelationKeys() { 684 setIgnoreInvalidCorrelationKeys(true); 685 return this; 686 } 687 688 /** 689 * Closes a correlation key when its complete. Any <i>late</i> received exchanges which has a correlation key 690 * that has been closed, it will be defined and a {@link ClosedCorrelationKeyException} 691 * is thrown. 692 * 693 * @param capacity the maximum capacity of the closed correlation key cache. 694 * Use <tt>0</tt> or negative value for unbounded capacity. 695 * @return builder 696 */ 697 public AggregateDefinition closeCorrelationKeyOnCompletion(int capacity) { 698 setCloseCorrelationKeyOnCompletion(capacity); 699 return this; 700 } 701 702 /** 703 * Discards the aggregated message on completion timeout. 704 * <p/> 705 * This means on timeout the aggregated message is dropped and not sent out of the aggregator. 706 * 707 * @return builder 708 */ 709 public AggregateDefinition discardOnCompletionTimeout() { 710 setDiscardOnCompletionTimeout(true); 711 return this; 712 } 713 714 /** 715 * Enables the batch completion mode where we aggregate from a {@link org.apache.camel.BatchConsumer} 716 * and aggregate the total number of exchanges the {@link org.apache.camel.BatchConsumer} has reported 717 * as total by checking the exchange property {@link org.apache.camel.Exchange#BATCH_COMPLETE} when its complete. 718 * 719 * @return builder 720 */ 721 public AggregateDefinition completionFromBatchConsumer() { 722 setCompletionFromBatchConsumer(true); 723 return this; 724 } 725 726 /** 727 * Number of messages aggregated before the aggregation is complete. This option can be set as either 728 * a fixed value or using an Expression which allows you to evaluate a size dynamically - will use Integer as result. 729 * If both are set Camel will fallback to use the fixed value if the Expression result was null or 0. 730 * 731 * @param completionSize the completion size, must be a positive number 732 * @return builder 733 */ 734 public AggregateDefinition completionSize(int completionSize) { 735 setCompletionSize(completionSize); 736 return this; 737 } 738 739 /** 740 * Number of messages aggregated before the aggregation is complete. This option can be set as either 741 * a fixed value or using an Expression which allows you to evaluate a size dynamically - will use Integer as result. 742 * If both are set Camel will fallback to use the fixed value if the Expression result was null or 0. 743 * 744 * @param completionSize the completion size as an {@link org.apache.camel.Expression} which is evaluated as a {@link Integer} type 745 * @return builder 746 */ 747 public AggregateDefinition completionSize(Expression completionSize) { 748 setCompletionSizeExpression(new ExpressionSubElementDefinition(completionSize)); 749 return this; 750 } 751 752 /** 753 * A repeating period in millis by which the aggregator will complete all current aggregated exchanges. 754 * Camel has a background task which is triggered every period. You cannot use this option together 755 * with completionTimeout, only one of them can be used. 756 * 757 * @param completionInterval the interval in millis, must be a positive value 758 * @return the builder 759 */ 760 public AggregateDefinition completionInterval(long completionInterval) { 761 setCompletionInterval(completionInterval); 762 return this; 763 } 764 765 /** 766 * Time in millis that an aggregated exchange should be inactive before its complete (timeout). 767 * This option can be set as either a fixed value or using an Expression which allows you to evaluate 768 * a timeout dynamically - will use Long as result. 769 * If both are set Camel will fallback to use the fixed value if the Expression result was null or 0. 770 * You cannot use this option together with completionInterval, only one of the two can be used. 771 * 772 * @param completionTimeout the timeout in millis, must be a positive value 773 * @return the builder 774 */ 775 public AggregateDefinition completionTimeout(long completionTimeout) { 776 setCompletionTimeout(completionTimeout); 777 return this; 778 } 779 780 /** 781 * Time in millis that an aggregated exchange should be inactive before its complete (timeout). 782 * This option can be set as either a fixed value or using an Expression which allows you to evaluate 783 * a timeout dynamically - will use Long as result. 784 * If both are set Camel will fallback to use the fixed value if the Expression result was null or 0. 785 * You cannot use this option together with completionInterval, only one of the two can be used. 786 * 787 * @param completionTimeout the timeout as an {@link Expression} which is evaluated as a {@link Long} type 788 * @return the builder 789 */ 790 public AggregateDefinition completionTimeout(Expression completionTimeout) { 791 setCompletionTimeoutExpression(new ExpressionSubElementDefinition(completionTimeout)); 792 return this; 793 } 794 795 /** 796 * Sets the AggregationStrategy to use with a fluent builder. 797 */ 798 public AggregationStrategyClause<AggregateDefinition> aggregationStrategy() { 799 AggregationStrategyClause<AggregateDefinition> clause = new AggregationStrategyClause<>(this); 800 setAggregationStrategy(clause); 801 return clause; 802 } 803 804 /** 805 * Sets the AggregationStrategy to use with a fluent builder. 806 */ 807 public AggregationStrategyClause<AggregateDefinition> strategy() { 808 return aggregationStrategy(); 809 } 810 811 /** 812 * Sets the aggregate strategy to use 813 * 814 * @param aggregationStrategy the aggregate strategy to use 815 * @return the builder 816 */ 817 public AggregateDefinition strategy(AggregationStrategy aggregationStrategy) { 818 return aggregationStrategy(aggregationStrategy); 819 } 820 821 /** 822 * Sets the aggregate strategy to use 823 * 824 * @param aggregationStrategy the aggregate strategy to use 825 * @return the builder 826 */ 827 public AggregateDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) { 828 setAggregationStrategy(aggregationStrategy); 829 return this; 830 } 831 832 /** 833 * Sets the aggregate strategy to use 834 * 835 * @param aggregationStrategyRef reference to the strategy to lookup in the registry 836 * @return the builder 837 */ 838 public AggregateDefinition aggregationStrategyRef(String aggregationStrategyRef) { 839 setAggregationStrategyRef(aggregationStrategyRef); 840 return this; 841 } 842 843 /** 844 * Sets the method name to use when using a POJO as {@link AggregationStrategy}. 845 * 846 * @param methodName the method name to call 847 * @return the builder 848 */ 849 public AggregateDefinition aggregationStrategyMethodName(String methodName) { 850 setAggregationStrategyMethodName(methodName); 851 return this; 852 } 853 854 /** 855 * Sets allowing null when using a POJO as {@link AggregationStrategy}. 856 * 857 * @return the builder 858 */ 859 public AggregateDefinition aggregationStrategyMethodAllowNull() { 860 setStrategyMethodAllowNull(true); 861 return this; 862 } 863 864 /** 865 * Sets the custom aggregate repository to use. 866 * <p/> 867 * Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository} 868 * 869 * @param aggregationRepository the aggregate repository to use 870 * @return the builder 871 */ 872 public AggregateDefinition aggregationRepository(AggregationRepository aggregationRepository) { 873 setAggregationRepository(aggregationRepository); 874 return this; 875 } 876 877 /** 878 * Sets the custom aggregate repository to use 879 * <p/> 880 * Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository} 881 * 882 * @param aggregationRepositoryRef reference to the repository to lookup in the registry 883 * @return the builder 884 */ 885 public AggregateDefinition aggregationRepositoryRef(String aggregationRepositoryRef) { 886 setAggregationRepositoryRef(aggregationRepositoryRef); 887 return this; 888 } 889 890 /** 891 * Enables grouped exchanges, so the aggregator will group all aggregated exchanges into a single 892 * combined Exchange holding all the aggregated exchanges in a {@link java.util.List}. 893 * 894 * @deprecated use {@link GroupedExchangeAggregationStrategy} as aggregation strategy instead. 895 */ 896 @Deprecated 897 public AggregateDefinition groupExchanges() { 898 setGroupExchanges(true); 899 // must use eager check when using grouped exchanges 900 setEagerCheckCompletion(true); 901 return this; 902 } 903 904 /** 905 * A Predicate to indicate when an aggregated exchange is complete. 906 * If this is not specified and the AggregationStrategy object implements Predicate, 907 * the aggregationStrategy object will be used as the completionPredicate. 908 */ 909 public AggregateDefinition completionPredicate(@AsPredicate Predicate predicate) { 910 checkNoCompletedPredicate(); 911 setCompletionPredicate(new ExpressionSubElementDefinition(predicate)); 912 return this; 913 } 914 915 /** 916 * A Predicate to indicate when an aggregated exchange is complete. 917 * If this is not specified and the AggregationStrategy object implements Predicate, 918 * the aggregationStrategy object will be used as the completionPredicate. 919 */ 920 @AsPredicate 921 public PredicateClause<AggregateDefinition> completionPredicate() { 922 PredicateClause<AggregateDefinition> clause = new PredicateClause<>(this); 923 completionPredicate(clause); 924 return clause; 925 } 926 927 /** 928 * A Predicate to indicate when an aggregated exchange is complete. 929 * If this is not specified and the AggregationStrategy object implements Predicate, 930 * the aggregationStrategy object will be used as the completionPredicate. 931 */ 932 @AsPredicate 933 public PredicateClause<AggregateDefinition> completion() { 934 return completionPredicate(); 935 } 936 937 /** 938 * A Predicate to indicate when an aggregated exchange is complete. 939 * If this is not specified and the AggregationStrategy object implements Predicate, 940 * the aggregationStrategy object will be used as the completionPredicate. 941 */ 942 public AggregateDefinition completion(@AsPredicate Predicate predicate) { 943 return completionPredicate(predicate); 944 } 945 946 /** 947 * Indicates to complete all current aggregated exchanges when the context is stopped 948 */ 949 public AggregateDefinition forceCompletionOnStop() { 950 setForceCompletionOnStop(true); 951 return this; 952 } 953 954 /** 955 * Indicates to wait to complete all current and partial (pending) aggregated exchanges when the context is stopped. 956 * <p/> 957 * This also means that we will wait for all pending exchanges which are stored in the aggregation repository 958 * to complete so the repository is empty before we can stop. 959 * <p/> 960 * You may want to enable this when using the memory based aggregation repository that is memory based only, 961 * and do not store data on disk. When this option is enabled, then the aggregator is waiting to complete 962 * all those exchanges before its stopped, when stopping CamelContext or the route using it. 963 */ 964 public AggregateDefinition completeAllOnStop() { 965 setCompleteAllOnStop(true); 966 return this; 967 } 968 969 /** 970 * When aggregated are completed they are being send out of the aggregator. 971 * This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency. 972 * If no custom thread pool has been specified then Camel creates a default pool with 10 concurrent threads. 973 */ 974 public AggregateDefinition parallelProcessing() { 975 setParallelProcessing(true); 976 return this; 977 } 978 979 /** 980 * When aggregated are completed they are being send out of the aggregator. 981 * This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency. 982 * If no custom thread pool has been specified then Camel creates a default pool with 10 concurrent threads. 983 */ 984 public AggregateDefinition parallelProcessing(boolean parallelProcessing) { 985 setParallelProcessing(parallelProcessing); 986 return this; 987 } 988 989 /** 990 * Turns on using optimistic locking, which requires the aggregationRepository being used, 991 * is supporting this by implementing {@link org.apache.camel.spi.OptimisticLockingAggregationRepository}. 992 */ 993 public AggregateDefinition optimisticLocking() { 994 setOptimisticLocking(true); 995 return this; 996 } 997 998 /** 999 * Allows to configure retry settings when using optimistic locking. 1000 */ 1001 public AggregateDefinition optimisticLockRetryPolicy(OptimisticLockRetryPolicy policy) { 1002 setOptimisticLockRetryPolicy(policy); 1003 return this; 1004 } 1005 1006 /** 1007 * If using parallelProcessing you can specify a custom thread pool to be used. 1008 * In fact also if you are not using parallelProcessing this custom thread pool is used to send out aggregated exchanges as well. 1009 */ 1010 public AggregateDefinition executorService(ExecutorService executorService) { 1011 setExecutorService(executorService); 1012 return this; 1013 } 1014 1015 /** 1016 * If using parallelProcessing you can specify a custom thread pool to be used. 1017 * In fact also if you are not using parallelProcessing this custom thread pool is used to send out aggregated exchanges as well. 1018 */ 1019 public AggregateDefinition executorServiceRef(String executorServiceRef) { 1020 setExecutorServiceRef(executorServiceRef); 1021 return this; 1022 } 1023 1024 /** 1025 * If using either of the completionTimeout, completionTimeoutExpression, or completionInterval options a 1026 * background thread is created to check for the completion for every aggregator. 1027 * Set this option to provide a custom thread pool to be used rather than creating a new thread for every aggregator. 1028 */ 1029 public AggregateDefinition timeoutCheckerExecutorService(ScheduledExecutorService executorService) { 1030 setTimeoutCheckerExecutorService(executorService); 1031 return this; 1032 } 1033 1034 /** 1035 * If using either of the completionTimeout, completionTimeoutExpression, or completionInterval options a 1036 * background thread is created to check for the completion for every aggregator. 1037 * Set this option to provide a custom thread pool to be used rather than creating a new thread for every aggregator. 1038 */ 1039 public AggregateDefinition timeoutCheckerExecutorServiceRef(String executorServiceRef) { 1040 setTimeoutCheckerExecutorServiceRef(executorServiceRef); 1041 return this; 1042 } 1043 1044 /** 1045 * To use a {@link org.apache.camel.processor.aggregate.AggregateController} to allow external sources to control 1046 * this aggregator. 1047 */ 1048 public AggregateDefinition aggregateController(AggregateController aggregateController) { 1049 setAggregateController(aggregateController); 1050 return this; 1051 } 1052 1053 // Section - Methods from ExpressionNode 1054 // Needed to copy methods from ExpressionNode here so that I could specify the 1055 // correlation expression as optional in JAXB 1056 1057 public ExpressionDefinition getExpression() { 1058 if (expression == null && correlationExpression != null) { 1059 expression = correlationExpression.getExpressionType(); 1060 } 1061 return expression; 1062 } 1063 1064 public void setExpression(ExpressionDefinition expression) { 1065 this.expression = expression; 1066 } 1067 1068 protected void checkNoCompletedPredicate() { 1069 if (getCompletionPredicate() != null) { 1070 throw new IllegalArgumentException("There is already a completionPredicate defined for this aggregator: " + this); 1071 } 1072 } 1073 1074 @Override 1075 public List<ProcessorDefinition<?>> getOutputs() { 1076 return outputs; 1077 } 1078 1079 public boolean isOutputSupported() { 1080 return true; 1081 } 1082 1083 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 1084 this.outputs = outputs; 1085 } 1086 1087}