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