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     */
017    package org.apache.camel.model;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.concurrent.ExecutorService;
022    
023    import javax.xml.bind.annotation.XmlAccessType;
024    import javax.xml.bind.annotation.XmlAccessorType;
025    import javax.xml.bind.annotation.XmlAttribute;
026    import javax.xml.bind.annotation.XmlRootElement;
027    import javax.xml.bind.annotation.XmlTransient;
028    
029    import org.apache.camel.Expression;
030    import org.apache.camel.Processor;
031    import org.apache.camel.model.language.ExpressionDefinition;
032    import org.apache.camel.processor.EvaluateExpressionProcessor;
033    import org.apache.camel.processor.Pipeline;
034    import org.apache.camel.processor.RecipientList;
035    import org.apache.camel.processor.aggregate.AggregationStrategy;
036    import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
037    import org.apache.camel.spi.RouteContext;
038    import org.apache.camel.util.CamelContextHelper;
039    
040    /**
041     * Represents an XML <recipientList/> element
042     *
043     * @version 
044     */
045    @XmlRootElement(name = "recipientList")
046    @XmlAccessorType(XmlAccessType.FIELD)
047    public class RecipientListDefinition<Type extends ProcessorDefinition<Type>> extends NoOutputExpressionNode implements ExecutorServiceAwareDefinition<RecipientListDefinition<Type>> {
048        @XmlTransient
049        private AggregationStrategy aggregationStrategy;
050        @XmlTransient
051        private ExecutorService executorService;
052        @XmlAttribute
053        private String delimiter;
054        @XmlAttribute
055        private Boolean parallelProcessing;
056        @XmlAttribute
057        private String strategyRef;
058        @XmlAttribute
059        private String executorServiceRef;
060        @XmlAttribute
061        private Boolean stopOnException;
062        @XmlAttribute
063        private Boolean ignoreInvalidEndpoints;
064        @XmlAttribute
065        private Boolean streaming;
066        @XmlAttribute
067        private Long timeout;
068        @XmlAttribute
069        private String onPrepareRef;
070        @XmlTransient
071        private Processor onPrepare;
072        @XmlAttribute
073        private Boolean shareUnitOfWork;
074    
075        public RecipientListDefinition() {
076        }
077    
078        public RecipientListDefinition(ExpressionDefinition expression) {
079            super(expression);
080        }
081    
082        public RecipientListDefinition(Expression expression) {
083            super(expression);
084        }
085    
086        @Override
087        public String toString() {
088            return "RecipientList[" + getExpression() + "]";
089        }
090    
091        @Override
092        public String getShortName() {
093            return "recipientList";
094        }
095        
096        @Override
097        public String getLabel() {
098            return "recipientList[" + getExpression() + "]";
099        }
100    
101        @Override
102        public Processor createProcessor(RouteContext routeContext) throws Exception {
103            final Expression expression = getExpression().createExpression(routeContext);
104    
105            RecipientList answer;
106            if (delimiter != null) {
107                answer = new RecipientList(routeContext.getCamelContext(), expression, delimiter);
108            } else {
109                answer = new RecipientList(routeContext.getCamelContext(), expression);
110            }
111            answer.setAggregationStrategy(createAggregationStrategy(routeContext));
112            answer.setParallelProcessing(isParallelProcessing());
113            answer.setStreaming(isStreaming());   
114            answer.setShareUnitOfWork(isShareUnitOfWork());
115            if (onPrepareRef != null) {
116                onPrepare = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), onPrepareRef, Processor.class);
117            }
118            if (onPrepare != null) {
119                answer.setOnPrepare(onPrepare);
120            }
121            if (stopOnException != null) {
122                answer.setStopOnException(isStopOnException());
123            }
124            if (ignoreInvalidEndpoints != null) {
125                answer.setIgnoreInvalidEndpoints(ignoreInvalidEndpoints);
126            }
127            if (getTimeout() != null) {
128                answer.setTimeout(getTimeout());
129            }
130    
131            boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isParallelProcessing());
132            ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "RecipientList", this, isParallelProcessing());
133            answer.setExecutorService(threadPool);
134            answer.setShutdownExecutorService(shutdownThreadPool);
135            long timeout = getTimeout() != null ? getTimeout() : 0;
136            if (timeout > 0 && !isParallelProcessing()) {
137                throw new IllegalArgumentException("Timeout is used but ParallelProcessing has not been enabled.");
138            }
139    
140            // create a pipeline with two processors
141            // the first is the eval processor which evaluates the expression to use
142            // the second is the recipient list
143            List<Processor> pipe = new ArrayList<Processor>(2);
144    
145            // the eval processor must be wrapped in error handler, so in case there was an
146            // error during evaluation, the error handler can deal with it
147            // the recipient list is not in error handler, as its has its own special error handling
148            // when sending to the recipients individually
149            Processor evalProcessor = new EvaluateExpressionProcessor(expression);
150            evalProcessor = super.wrapInErrorHandler(routeContext, evalProcessor);
151    
152            pipe.add(evalProcessor);
153            pipe.add(answer);
154    
155            // wrap in nested pipeline so this appears as one processor
156            // (threads definition does this as well)
157            return new Pipeline(routeContext.getCamelContext(), pipe) {
158                @Override
159                public String toString() {
160                    return "RecipientList[" + expression + "]";
161                }
162            };
163        }
164        
165        private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
166            if (aggregationStrategy == null && strategyRef != null) {
167                aggregationStrategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
168            }
169            if (aggregationStrategy == null) {
170                // fallback to use latest
171                aggregationStrategy = new UseLatestAggregationStrategy();
172            }
173            return aggregationStrategy;
174        }
175    
176        // Fluent API
177        // -------------------------------------------------------------------------
178    
179        @Override
180        @SuppressWarnings("unchecked")
181        public Type end() {
182            // allow end() to return to previous type so you can continue in the DSL
183            return (Type) super.end();
184        }
185    
186        /**
187         * Set the aggregationStrategy
188         *
189         * @param aggregationStrategy the strategy
190         * @return the builder
191         */
192        public RecipientListDefinition<Type> aggregationStrategy(AggregationStrategy aggregationStrategy) {
193            setAggregationStrategy(aggregationStrategy);
194            return this;
195        }
196    
197        /**
198         * Set the aggregationStrategy
199         *
200         * @param aggregationStrategyRef a reference to a strategy to lookup
201         * @return the builder
202         */
203        public RecipientListDefinition<Type> aggregationStrategyRef(String aggregationStrategyRef) {
204            setStrategyRef(aggregationStrategyRef);
205            return this;
206        }
207        
208        /**
209         * Ignore the invalidate endpoint exception when try to create a producer with that endpoint
210         *
211         * @return the builder
212         */
213        public RecipientListDefinition<Type> ignoreInvalidEndpoints() {
214            setIgnoreInvalidEndpoints(true);
215            return this;
216        }
217    
218        /**
219         * Doing the recipient list work in parallel
220         *
221         * @return the builder
222         */
223        public RecipientListDefinition<Type> parallelProcessing() {
224            setParallelProcessing(true);
225            return this;
226        }
227        
228        /**
229         * Doing the recipient list work in streaming model
230         *
231         * @return the builder
232         */
233        public RecipientListDefinition<Type> streaming() {
234            setStreaming(true);
235            return this;
236        }
237    
238        /**
239         * Will now stop further processing if an exception or failure occurred during processing of an
240         * {@link org.apache.camel.Exchange} and the caused exception will be thrown.
241         * <p/>
242         * Will also stop if processing the exchange failed (has a fault message) or an exception
243         * was thrown and handled by the error handler (such as using onException). In all situations
244         * the recipient list will stop further processing. This is the same behavior as in pipeline, which
245         * is used by the routing engine.
246         * <p/>
247         * The default behavior is to <b>not</b> stop but continue processing till the end
248         *
249         * @return the builder
250         */
251        public RecipientListDefinition<Type> stopOnException() {
252            setStopOnException(true);
253            return this;
254        }
255    
256        public RecipientListDefinition<Type> executorService(ExecutorService executorService) {
257            setExecutorService(executorService);
258            return this;
259        }
260    
261        public RecipientListDefinition<Type> executorServiceRef(String executorServiceRef) {
262            setExecutorServiceRef(executorServiceRef);
263            return this;
264        }
265    
266        /**
267         * Uses the {@link Processor} when preparing the {@link org.apache.camel.Exchange} to be used send.
268         * This can be used to deep-clone messages that should be send, or any custom logic needed before
269         * the exchange is send.
270         *
271         * @param onPrepare the processor
272         * @return the builder
273         */
274        public RecipientListDefinition<Type> onPrepare(Processor onPrepare) {
275            setOnPrepare(onPrepare);
276            return this;
277        }
278    
279        /**
280         * Uses the {@link Processor} when preparing the {@link org.apache.camel.Exchange} to be send.
281         * This can be used to deep-clone messages that should be send, or any custom logic needed before
282         * the exchange is send.
283         *
284         * @param onPrepareRef reference to the processor to lookup in the {@link org.apache.camel.spi.Registry}
285         * @return the builder
286         */
287        public RecipientListDefinition<Type> onPrepareRef(String onPrepareRef) {
288            setOnPrepareRef(onPrepareRef);
289            return this;
290        }
291    
292        /**
293         * Sets a timeout value in millis to use when using parallelProcessing.
294         *
295         * @param timeout timeout in millis
296         * @return the builder
297         */
298        public RecipientListDefinition<Type> timeout(long timeout) {
299            setTimeout(timeout);
300            return this;
301        }
302    
303        /**
304         * Shares the {@link org.apache.camel.spi.UnitOfWork} with the parent and each of the sub messages.
305         *
306         * @return the builder.
307         * @see org.apache.camel.spi.SubUnitOfWork
308         */
309        public RecipientListDefinition<Type> shareUnitOfWork() {
310            setShareUnitOfWork(true);
311            return this;
312        }
313    
314        // Properties
315        //-------------------------------------------------------------------------
316    
317        public String getDelimiter() {
318            return delimiter;
319        }
320    
321        public void setDelimiter(String delimiter) {
322            this.delimiter = delimiter;
323        }
324    
325        public Boolean getParallelProcessing() {
326            return parallelProcessing;
327        }
328    
329        public void setParallelProcessing(Boolean parallelProcessing) {
330            this.parallelProcessing = parallelProcessing;
331        }
332    
333        public boolean isParallelProcessing() {
334            return parallelProcessing != null && parallelProcessing;
335        }
336    
337        public String getStrategyRef() {
338            return strategyRef;
339        }
340    
341        public void setStrategyRef(String strategyRef) {
342            this.strategyRef = strategyRef;
343        }
344    
345        public String getExecutorServiceRef() {
346            return executorServiceRef;
347        }
348    
349        public void setExecutorServiceRef(String executorServiceRef) {
350            this.executorServiceRef = executorServiceRef;
351        }
352    
353        public Boolean getIgnoreInvalidEndpoints() {
354            return ignoreInvalidEndpoints;
355        }
356    
357        public void setIgnoreInvalidEndpoints(Boolean ignoreInvalidEndpoints) {
358            this.ignoreInvalidEndpoints = ignoreInvalidEndpoints;
359        }
360    
361        public boolean isIgnoreInvalidEndpoints() {
362            return ignoreInvalidEndpoints != null && ignoreInvalidEndpoints;
363        }
364    
365        public Boolean getStopOnException() {
366            return stopOnException;
367        }
368    
369        public void setStopOnException(Boolean stopOnException) {
370            this.stopOnException = stopOnException;
371        }
372    
373        public boolean isStopOnException() {
374            return stopOnException != null && stopOnException;
375        }
376    
377        public AggregationStrategy getAggregationStrategy() {
378            return aggregationStrategy;
379        }
380    
381        public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
382            this.aggregationStrategy = aggregationStrategy;
383        }
384    
385        public ExecutorService getExecutorService() {
386            return executorService;
387        }
388    
389        public void setExecutorService(ExecutorService executorService) {
390            this.executorService = executorService;
391        }
392    
393        public Boolean getStreaming() {
394            return streaming;
395        }
396    
397        public void setStreaming(Boolean streaming) {
398            this.streaming = streaming;
399        }
400    
401        public boolean isStreaming() {
402            return streaming != null && streaming;
403        }
404    
405        public Long getTimeout() {
406            return timeout;
407        }
408    
409        public void setTimeout(Long timeout) {
410            this.timeout = timeout;
411        }
412    
413        public String getOnPrepareRef() {
414            return onPrepareRef;
415        }
416    
417        public void setOnPrepareRef(String onPrepareRef) {
418            this.onPrepareRef = onPrepareRef;
419        }
420    
421        public Processor getOnPrepare() {
422            return onPrepare;
423        }
424    
425        public void setOnPrepare(Processor onPrepare) {
426            this.onPrepare = onPrepare;
427        }
428    
429        public Boolean getShareUnitOfWork() {
430            return shareUnitOfWork;
431        }
432    
433        public void setShareUnitOfWork(Boolean shareUnitOfWork) {
434            this.shareUnitOfWork = shareUnitOfWork;
435        }
436    
437        public boolean isShareUnitOfWork() {
438            return shareUnitOfWork != null && shareUnitOfWork;
439        }
440    
441    }