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