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