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