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