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.concurrent.ExecutorService; 020import java.util.function.Supplier; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlAttribute; 025import javax.xml.bind.annotation.XmlRootElement; 026import javax.xml.bind.annotation.XmlTransient; 027 028import org.apache.camel.AggregationStrategy; 029import org.apache.camel.Expression; 030import org.apache.camel.Processor; 031import org.apache.camel.builder.ProcessClause; 032import org.apache.camel.model.language.ExpressionDefinition; 033import org.apache.camel.spi.Metadata; 034 035/** 036 * Routes messages to a number of dynamically specified recipients (dynamic to) 037 */ 038@Metadata(label = "eip,endpoint,routing") 039@XmlRootElement(name = "recipientList") 040@XmlAccessorType(XmlAccessType.FIELD) 041public class RecipientListDefinition<Type extends ProcessorDefinition<Type>> extends ExpressionNode implements ExecutorServiceAwareDefinition<RecipientListDefinition<Type>> { 042 @XmlTransient 043 private AggregationStrategy aggregationStrategy; 044 @XmlTransient 045 private ExecutorService executorService; 046 @XmlAttribute 047 @Metadata(defaultValue = ",") 048 private String delimiter; 049 @XmlAttribute 050 @Metadata(javaType = "java.lang.Boolean") 051 private String parallelProcessing; 052 @XmlAttribute 053 private String strategyRef; 054 @XmlAttribute 055 private String strategyMethodName; 056 @XmlAttribute 057 @Metadata(javaType = "java.lang.Boolean") 058 private String strategyMethodAllowNull; 059 @XmlAttribute 060 private String executorServiceRef; 061 @XmlAttribute 062 @Metadata(javaType = "java.lang.Boolean") 063 private String stopOnException; 064 @XmlAttribute 065 @Metadata(javaType = "java.lang.Boolean") 066 private String ignoreInvalidEndpoints; 067 @XmlAttribute 068 @Metadata(javaType = "java.lang.Boolean") 069 private String streaming; 070 @XmlAttribute 071 @Metadata(javaType = "java.time.Duration", defaultValue = "0") 072 private String timeout; 073 @XmlAttribute 074 private String onPrepareRef; 075 @XmlTransient 076 private Processor onPrepare; 077 @XmlAttribute 078 @Metadata(javaType = "java.lang.Boolean") 079 private String shareUnitOfWork; 080 @XmlAttribute 081 @Metadata(javaType = "java.lang.Integer") 082 private String cacheSize; 083 @XmlAttribute 084 @Metadata(javaType = "java.lang.Boolean") 085 private String parallelAggregate; 086 @XmlAttribute 087 @Metadata(javaType = "java.lang.Boolean") 088 private String stopOnAggregateException; 089 090 public RecipientListDefinition() { 091 } 092 093 public RecipientListDefinition(ExpressionDefinition expression) { 094 super(expression); 095 } 096 097 public RecipientListDefinition(Expression expression) { 098 super(expression); 099 } 100 101 @Override 102 public String toString() { 103 return "RecipientList[" + getExpression() + "]"; 104 } 105 106 @Override 107 public String getShortName() { 108 return "recipientList"; 109 } 110 111 @Override 112 public String getLabel() { 113 return "recipientList[" + getExpression() + "]"; 114 } 115 116 // Fluent API 117 // ------------------------------------------------------------------------- 118 119 @Override 120 @SuppressWarnings("unchecked") 121 public Type end() { 122 // allow end() to return to previous type so you can continue in the DSL 123 return (Type)super.end(); 124 } 125 126 /** 127 * Delimiter used if the Expression returned multiple endpoints. Can be 128 * turned off using the value <tt>false</tt>. 129 * <p/> 130 * The default value is , 131 * 132 * @param delimiter the delimiter 133 * @return the builder 134 */ 135 public RecipientListDefinition<Type> delimiter(String delimiter) { 136 setDelimiter(delimiter); 137 return this; 138 } 139 140 /** 141 * Sets the AggregationStrategy to be used to assemble the replies from the 142 * recipients, into a single outgoing message from the RecipientList. By 143 * default Camel will use the last reply as the outgoing message. You can 144 * also use a POJO as the AggregationStrategy 145 */ 146 public RecipientListDefinition<Type> aggregationStrategy(AggregationStrategy aggregationStrategy) { 147 setAggregationStrategy(aggregationStrategy); 148 return this; 149 } 150 151 /** 152 * Sets the AggregationStrategy to be used to assemble the replies from the 153 * recipients, into a single outgoing message from the RecipientList. By 154 * default Camel will use the last reply as the outgoing message. You can 155 * also use a POJO as the AggregationStrategy 156 */ 157 public RecipientListDefinition<Type> aggregationStrategy(Supplier<AggregationStrategy> aggregationStrategy) { 158 setAggregationStrategy(aggregationStrategy.get()); 159 return this; 160 } 161 162 /** 163 * Sets a reference to the AggregationStrategy to be used to assemble the 164 * replies from the recipients, into a single outgoing message from the 165 * RecipientList. By default Camel will use the last reply as the outgoing 166 * message. You can also use a POJO as the AggregationStrategy 167 */ 168 public RecipientListDefinition<Type> aggregationStrategyRef(String aggregationStrategyRef) { 169 setStrategyRef(aggregationStrategyRef); 170 return this; 171 } 172 173 /** 174 * This option can be used to explicit declare the method name to use, when 175 * using POJOs as the AggregationStrategy. 176 * 177 * @param methodName the method name to call 178 * @return the builder 179 */ 180 public RecipientListDefinition<Type> aggregationStrategyMethodName(String methodName) { 181 setStrategyMethodName(methodName); 182 return this; 183 } 184 185 /** 186 * If this option is false then the aggregate method is not used if there 187 * was no data to enrich. If this option is true then null values is used as 188 * the oldExchange (when no data to enrich), when using POJOs as the 189 * AggregationStrategy 190 * 191 * @return the builder 192 */ 193 public RecipientListDefinition<Type> aggregationStrategyMethodAllowNull() { 194 setStrategyMethodAllowNull(Boolean.toString(true)); 195 return this; 196 } 197 198 /** 199 * Ignore the invalidate endpoint exception when try to create a producer 200 * with that endpoint 201 * 202 * @return the builder 203 */ 204 public RecipientListDefinition<Type> ignoreInvalidEndpoints() { 205 setIgnoreInvalidEndpoints(Boolean.toString(true)); 206 return this; 207 } 208 209 /** 210 * If enabled then sending messages to the recipients occurs concurrently. 211 * Note the caller thread will still wait until all messages has been fully 212 * processed, before it continues. Its only the sending and processing the 213 * replies from the recipients which happens concurrently. 214 * 215 * @return the builder 216 */ 217 public RecipientListDefinition<Type> parallelProcessing() { 218 setParallelProcessing(Boolean.toString(true)); 219 return this; 220 } 221 222 /** 223 * If enabled then sending messages to the recipients occurs concurrently. 224 * Note the caller thread will still wait until all messages has been fully 225 * processed, before it continues. Its only the sending and processing the 226 * replies from the recipients which happens concurrently. 227 * 228 * @return the builder 229 */ 230 public RecipientListDefinition<Type> parallelProcessing(boolean parallelProcessing) { 231 setParallelProcessing(Boolean.toString(parallelProcessing)); 232 return this; 233 } 234 235 /** 236 * If enabled then the aggregate method on AggregationStrategy can be called 237 * concurrently. Notice that this would require the implementation of 238 * AggregationStrategy to be implemented as thread-safe. By default this is 239 * false meaning that Camel synchronizes the call to the aggregate method. 240 * Though in some use-cases this can be used to archive higher performance 241 * when the AggregationStrategy is implemented as thread-safe. 242 * 243 * @return the builder 244 */ 245 public RecipientListDefinition<Type> parallelAggregate() { 246 setParallelAggregate(Boolean.toString(true)); 247 return this; 248 } 249 250 /** 251 * If enabled, unwind exceptions occurring at aggregation time to the error 252 * handler when parallelProcessing is used. Currently, aggregation time 253 * exceptions do not stop the route processing when parallelProcessing is 254 * used. Enabling this option allows to work around this behavior. The 255 * default value is <code>false</code> for the sake of backward 256 * compatibility. 257 * 258 * @return the builder 259 */ 260 public RecipientListDefinition<Type> stopOnAggregateException() { 261 setStopOnAggregateException(Boolean.toString(true)); 262 return this; 263 } 264 265 /** 266 * If enabled then Camel will process replies out-of-order, eg in the order 267 * they come back. If disabled, Camel will process replies in the same order 268 * as defined by the recipient list. 269 * 270 * @return the builder 271 */ 272 public RecipientListDefinition<Type> streaming() { 273 setStreaming(Boolean.toString(true)); 274 return this; 275 } 276 277 /** 278 * Will now stop further processing if an exception or failure occurred 279 * during processing of an {@link org.apache.camel.Exchange} and the caused 280 * exception will be thrown. 281 * <p/> 282 * Will also stop if processing the exchange failed (has a fault message) or 283 * an exception was thrown and handled by the error handler (such as using 284 * onException). In all situations the recipient list will stop further 285 * processing. This is the same behavior as in pipeline, which is used by 286 * the routing engine. 287 * <p/> 288 * The default behavior is to <b>not</b> stop but continue processing till 289 * the end 290 * 291 * @return the builder 292 */ 293 public RecipientListDefinition<Type> stopOnException() { 294 setStopOnException(Boolean.toString(true)); 295 return this; 296 } 297 298 /** 299 * To use a custom Thread Pool to be used for parallel processing. Notice if 300 * you set this option, then parallel processing is automatic implied, and 301 * you do not have to enable that option as well. 302 */ 303 @Override 304 public RecipientListDefinition<Type> executorService(ExecutorService executorService) { 305 setExecutorService(executorService); 306 return this; 307 } 308 309 /** 310 * Refers to a custom Thread Pool to be used for parallel processing. Notice 311 * if you set this option, then parallel processing is automatic implied, 312 * and you do not have to enable that option as well. 313 */ 314 @Override 315 public RecipientListDefinition<Type> executorServiceRef(String executorServiceRef) { 316 setExecutorServiceRef(executorServiceRef); 317 return this; 318 } 319 320 /** 321 * Uses the {@link Processor} when preparing the 322 * {@link org.apache.camel.Exchange} to be used send. This can be used to 323 * deep-clone messages that should be send, or any custom logic needed 324 * before the exchange is send. 325 * 326 * @param onPrepare the processor 327 * @return the builder 328 */ 329 public RecipientListDefinition<Type> onPrepare(Processor onPrepare) { 330 setOnPrepare(onPrepare); 331 return this; 332 } 333 334 /** 335 * Sets the {@link Processor} when preparing the 336 * {@link org.apache.camel.Exchange} to be used send using a fluent buidler. 337 */ 338 public ProcessClause<RecipientListDefinition<Type>> onPrepare() { 339 ProcessClause<RecipientListDefinition<Type>> clause = new ProcessClause<>(this); 340 setOnPrepare(clause); 341 return clause; 342 } 343 344 /** 345 * Uses the {@link Processor} when preparing the 346 * {@link org.apache.camel.Exchange} to be send. This can be used to 347 * deep-clone messages that should be send, or any custom logic needed 348 * before the exchange is send. 349 * 350 * @param onPrepareRef reference to the processor to lookup in the 351 * {@link org.apache.camel.spi.Registry} 352 * @return the builder 353 */ 354 public RecipientListDefinition<Type> onPrepareRef(String onPrepareRef) { 355 setOnPrepareRef(onPrepareRef); 356 return this; 357 } 358 359 /** 360 * Sets a total timeout specified in millis, when using parallel processing. 361 * If the Recipient List hasn't been able to send and process all replies 362 * within the given timeframe, then the timeout triggers and the Recipient 363 * List breaks out and continues. Notice if you provide a 364 * TimeoutAwareAggregationStrategy then the timeout method is invoked before 365 * breaking out. If the timeout is reached with running tasks still 366 * remaining, certain tasks for which it is difficult for Camel to shut down 367 * in a graceful manner may continue to run. So use this option with a bit 368 * of care. 369 * 370 * @param timeout timeout in millis 371 * @return the builder 372 */ 373 public RecipientListDefinition<Type> timeout(long timeout) { 374 setTimeout(Long.toString(timeout)); 375 return this; 376 } 377 378 /** 379 * Shares the {@link org.apache.camel.spi.UnitOfWork} with the parent and 380 * each of the sub messages. Recipient List will by default not share unit 381 * of work between the parent exchange and each recipient exchange. This 382 * means each sub exchange has its own individual unit of work. 383 * 384 * @return the builder. 385 */ 386 public RecipientListDefinition<Type> shareUnitOfWork() { 387 setShareUnitOfWork(Boolean.toString(true)); 388 return this; 389 } 390 391 /** 392 * Sets the maximum size used by the 393 * {@link org.apache.camel.spi.ProducerCache} which is used to cache and 394 * reuse producers when using this recipient list, when uris are reused. 395 * 396 * Beware that when using dynamic endpoints then it affects how well the cache can be utilized. 397 * If each dynamic endpoint is unique then its best to turn of caching by setting this to -1, which 398 * allows Camel to not cache both the producers and endpoints; they are regarded as prototype scoped 399 * and will be stopped and discarded after use. This reduces memory usage as otherwise producers/endpoints 400 * are stored in memory in the caches. 401 * 402 * However if there are a high degree of dynamic endpoints that have been used before, then it can 403 * benefit to use the cache to reuse both producers and endpoints and therefore the cache size 404 * can be set accordingly or rely on the default size (1000). 405 * 406 * If there is a mix of unique and used before dynamic endpoints, then setting a reasonable cache size 407 * can help reduce memory usage to avoid storing too many non frequent used producers. 408 * 409 * @param cacheSize the cache size, use <tt>0</tt> for default cache size, 410 * or <tt>-1</tt> to turn cache off. 411 * @return the builder 412 */ 413 public RecipientListDefinition<Type> cacheSize(int cacheSize) { 414 setCacheSize(Integer.toString(cacheSize)); 415 return this; 416 } 417 418 /** 419 * Sets the maximum size used by the 420 * {@link org.apache.camel.spi.ProducerCache} which is used to cache and 421 * reuse producers when using this recipient list, when uris are reused. 422 * 423 * Beware that when using dynamic endpoints then it affects how well the cache can be utilized. 424 * If each dynamic endpoint is unique then its best to turn of caching by setting this to -1, which 425 * allows Camel to not cache both the producers and endpoints; they are regarded as prototype scoped 426 * and will be stopped and discarded after use. This reduces memory usage as otherwise producers/endpoints 427 * are stored in memory in the caches. 428 * 429 * However if there are a high degree of dynamic endpoints that have been used before, then it can 430 * benefit to use the cache to reuse both producers and endpoints and therefore the cache size 431 * can be set accordingly or rely on the default size (1000). 432 * 433 * If there is a mix of unique and used before dynamic endpoints, then setting a reasonable cache size 434 * can help reduce memory usage to avoid storing too many non frequent used producers. 435 * 436 * @param cacheSize the cache size, use <tt>0</tt> for default cache size, 437 * or <tt>-1</tt> to turn cache off. 438 * @return the builder 439 */ 440 public RecipientListDefinition<Type> cacheSize(String cacheSize) { 441 setCacheSize(cacheSize); 442 return this; 443 } 444 445 // Properties 446 // ------------------------------------------------------------------------- 447 448 /** 449 * Expression that returns which endpoints (url) to send the message to (the 450 * recipients). If the expression return an empty value then the message is 451 * not sent to any recipients. 452 */ 453 @Override 454 public void setExpression(ExpressionDefinition expression) { 455 // override to include javadoc what the expression is used for 456 super.setExpression(expression); 457 } 458 459 public String getDelimiter() { 460 return delimiter; 461 } 462 463 public void setDelimiter(String delimiter) { 464 this.delimiter = delimiter; 465 } 466 467 public String getParallelProcessing() { 468 return parallelProcessing; 469 } 470 471 public void setParallelProcessing(String parallelProcessing) { 472 this.parallelProcessing = parallelProcessing; 473 } 474 475 public String getStrategyRef() { 476 return strategyRef; 477 } 478 479 /** 480 * Sets a reference to the AggregationStrategy to be used to assemble the 481 * replies from the recipients, into a single outgoing message from the 482 * RecipientList. By default Camel will use the last reply as the outgoing 483 * message. You can also use a POJO as the AggregationStrategy 484 */ 485 public void setStrategyRef(String strategyRef) { 486 this.strategyRef = strategyRef; 487 } 488 489 public String getStrategyMethodName() { 490 return strategyMethodName; 491 } 492 493 /** 494 * This option can be used to explicit declare the method name to use, when 495 * using POJOs as the AggregationStrategy. 496 */ 497 public void setStrategyMethodName(String strategyMethodName) { 498 this.strategyMethodName = strategyMethodName; 499 } 500 501 public String getStrategyMethodAllowNull() { 502 return strategyMethodAllowNull; 503 } 504 505 /** 506 * If this option is false then the aggregate method is not used if there 507 * was no data to enrich. If this option is true then null values is used as 508 * the oldExchange (when no data to enrich), when using POJOs as the 509 * AggregationStrategy 510 */ 511 public void setStrategyMethodAllowNull(String strategyMethodAllowNull) { 512 this.strategyMethodAllowNull = strategyMethodAllowNull; 513 } 514 515 @Override 516 public String getExecutorServiceRef() { 517 return executorServiceRef; 518 } 519 520 @Override 521 public void setExecutorServiceRef(String executorServiceRef) { 522 this.executorServiceRef = executorServiceRef; 523 } 524 525 public String getIgnoreInvalidEndpoints() { 526 return ignoreInvalidEndpoints; 527 } 528 529 public void setIgnoreInvalidEndpoints(String ignoreInvalidEndpoints) { 530 this.ignoreInvalidEndpoints = ignoreInvalidEndpoints; 531 } 532 533 public String getStopOnException() { 534 return stopOnException; 535 } 536 537 public void setStopOnException(String stopOnException) { 538 this.stopOnException = stopOnException; 539 } 540 541 public AggregationStrategy getAggregationStrategy() { 542 return aggregationStrategy; 543 } 544 545 /** 546 * Sets the AggregationStrategy to be used to assemble the replies from the 547 * recipients, into a single outgoing message from the RecipientList. By 548 * default Camel will use the last reply as the outgoing message. You can 549 * also use a POJO as the AggregationStrategy 550 */ 551 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) { 552 this.aggregationStrategy = aggregationStrategy; 553 } 554 555 @Override 556 public ExecutorService getExecutorService() { 557 return executorService; 558 } 559 560 @Override 561 public void setExecutorService(ExecutorService executorService) { 562 this.executorService = executorService; 563 } 564 565 public String getStreaming() { 566 return streaming; 567 } 568 569 public void setStreaming(String streaming) { 570 this.streaming = streaming; 571 } 572 573 public String getTimeout() { 574 return timeout; 575 } 576 577 public void setTimeout(String timeout) { 578 this.timeout = timeout; 579 } 580 581 public String getOnPrepareRef() { 582 return onPrepareRef; 583 } 584 585 public void setOnPrepareRef(String onPrepareRef) { 586 this.onPrepareRef = onPrepareRef; 587 } 588 589 public Processor getOnPrepare() { 590 return onPrepare; 591 } 592 593 public void setOnPrepare(Processor onPrepare) { 594 this.onPrepare = onPrepare; 595 } 596 597 public String getShareUnitOfWork() { 598 return shareUnitOfWork; 599 } 600 601 public void setShareUnitOfWork(String shareUnitOfWork) { 602 this.shareUnitOfWork = shareUnitOfWork; 603 } 604 605 public String getCacheSize() { 606 return cacheSize; 607 } 608 609 public void setCacheSize(String cacheSize) { 610 this.cacheSize = cacheSize; 611 } 612 613 public String getParallelAggregate() { 614 return parallelAggregate; 615 } 616 617 public void setParallelAggregate(String parallelAggregate) { 618 this.parallelAggregate = parallelAggregate; 619 } 620 621 public String getStopOnAggregateException() { 622 return stopOnAggregateException; 623 } 624 625 public void setStopOnAggregateException(String stopOnAggregateException) { 626 this.stopOnAggregateException = stopOnAggregateException; 627 } 628}