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.concurrent.ExecutorService;
020    import javax.xml.bind.annotation.XmlAccessType;
021    import javax.xml.bind.annotation.XmlAccessorType;
022    import javax.xml.bind.annotation.XmlAttribute;
023    import javax.xml.bind.annotation.XmlRootElement;
024    import javax.xml.bind.annotation.XmlTransient;
025    
026    import org.apache.camel.Expression;
027    import org.apache.camel.Processor;
028    import org.apache.camel.builder.ExpressionClause;
029    import org.apache.camel.model.language.ExpressionDefinition;
030    import org.apache.camel.processor.Splitter;
031    import org.apache.camel.processor.aggregate.AggregationStrategy;
032    import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
033    import org.apache.camel.spi.RouteContext;
034    import org.apache.camel.util.concurrent.ExecutorServiceHelper;
035    
036    /**
037     * Represents an XML <split/> element
038     *
039     * @version $Revision: 887262 $
040     */
041    @XmlRootElement(name = "split")
042    @XmlAccessorType(XmlAccessType.FIELD)
043    public class SplitDefinition extends ExpressionNode {
044        @XmlTransient
045        private AggregationStrategy aggregationStrategy;
046        @XmlTransient
047        private ExecutorService executorService;
048        @XmlAttribute(required = false)
049        private Boolean parallelProcessing;
050        @XmlAttribute(required = false)
051        private String strategyRef;
052        @XmlAttribute(required = false)
053        private String executorServiceRef;
054        @XmlAttribute(required = false)
055        private Boolean streaming = false;
056        @XmlAttribute(required = false)
057        private Boolean stopOnException;
058    
059        public SplitDefinition() {
060        }
061    
062        public SplitDefinition(Expression expression) {
063            super(expression);
064        }
065    
066        public SplitDefinition(ExpressionDefinition expression) {
067            super(expression);
068        }
069    
070        @Override
071        public String toString() {
072            return "Split[" + getExpression() + " -> " + getOutputs() + "]";
073        }
074    
075        @Override
076        public String getShortName() {
077            return "split";
078        }
079    
080        @Override
081        public String getLabel() {
082            return "split";
083        }
084    
085        @Override
086        public Processor createProcessor(RouteContext routeContext) throws Exception {
087            Processor childProcessor = routeContext.createProcessor(this);
088            aggregationStrategy = createAggregationStrategy(routeContext);
089            executorService = createExecutorService(routeContext);
090            return new Splitter(getExpression().createExpression(routeContext), childProcessor, aggregationStrategy,
091                    isParallelProcessing(), executorService, isStreaming(), isStopOnException());
092        }
093    
094        
095        private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
096            AggregationStrategy strategy = getAggregationStrategy();
097            if (strategy == null && strategyRef != null) {
098                strategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
099            }
100            if (strategy == null) {
101                // fallback to use latest
102                strategy = new UseLatestAggregationStrategy();
103            }
104            return strategy;
105        }        
106        
107        private ExecutorService createExecutorService(RouteContext routeContext) {
108            if (executorServiceRef != null) {
109                executorService = routeContext.lookup(executorServiceRef, ExecutorService.class);
110            }
111            if (executorService == null) {
112                executorService = ExecutorServiceHelper.newScheduledThreadPool(10, "Split", true);
113            }
114            return executorService;
115        }         
116        
117        // Fluent API
118        // -------------------------------------------------------------------------
119    
120        /**
121         * Set the expression that the splitter will use
122         *
123         * @return the builder
124         */
125        public ExpressionClause<SplitDefinition> expression() {
126            return ExpressionClause.createAndSetExpression(this);
127        }
128        /**
129         * Set the aggregationStrategy
130         *
131         * @return the builder
132         */
133        public SplitDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) {
134            setAggregationStrategy(aggregationStrategy);
135            return this;
136        }
137        
138        /**
139         * Doing the splitting work in parallel
140         *
141         * @return the builder
142         */
143        public SplitDefinition parallelProcessing() {
144            setParallelProcessing(true);
145            return this;
146        }
147        
148        /**
149         * Set the splitting action's thread model
150         *
151         * @param parallelProcessing <tt>true</tt> to use a thread pool, if <tt>false</tt> then work is done in the
152         * calling thread.
153         *
154         * @deprecated use #parallelProcessing instead
155         * @return the builder
156         */
157        @Deprecated
158        public SplitDefinition parallelProcessing(boolean parallelProcessing) {
159            setParallelProcessing(parallelProcessing);
160            return this;
161        }
162        
163        /**
164         * Enables streaming. 
165         * See {@link SplitDefinition#setStreaming(boolean)} for more information
166         *
167         * @return the builder
168         */
169        public SplitDefinition streaming() {
170            setStreaming(true);
171            return this;
172        }
173        
174        /**
175         * Will now stop further processing if an exception occurred during processing of an
176         * {@link org.apache.camel.Exchange} and the caused exception will be thrown.
177         * <p/>
178         * The default behavior is to <b>not</b> stop but continue processing till the end
179         *
180         * @return the builder
181         */
182        public SplitDefinition stopOnException() {
183            setStopOnException(true);
184            return this;
185        }
186    
187        /**
188         * Setting the executor service for executing the splitting action.
189         *
190         * @param executorService the executor service
191         * @return the builder
192         */
193        public SplitDefinition executorService(ExecutorService executorService) {
194            setExecutorService(executorService);
195            return this;
196        }
197        
198        // Properties
199        //-------------------------------------------------------------------------
200    
201        public AggregationStrategy getAggregationStrategy() {
202            return aggregationStrategy;
203        }
204    
205        public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
206            this.aggregationStrategy = aggregationStrategy;
207        }
208        
209        public boolean isParallelProcessing() {
210            return parallelProcessing != null ? parallelProcessing : false;
211        }
212    
213        public void setParallelProcessing(boolean parallelProcessing) {
214            this.parallelProcessing = parallelProcessing;
215        }
216        
217        /**
218         * The splitter should use streaming -- exchanges are being sent as the data for them becomes available.
219         * This improves throughput and memory usage, but it has a drawback: 
220         * - the sent exchanges will no longer contain the {@link org.apache.camel.Exchange#SPLIT_SIZE} header property
221         * 
222         * @return whether or not streaming should be used
223         */
224        public boolean isStreaming() {
225            return streaming != null ? streaming : false;
226        }
227    
228        public void setStreaming(boolean streaming) {
229            this.streaming = streaming;
230        }
231    
232        public Boolean isStopOnException() {
233            return stopOnException != null ? stopOnException : false;
234        }
235    
236        public void setStopOnException(Boolean stopOnException) {
237            this.stopOnException = stopOnException;
238        }
239    
240        public ExecutorService getExecutorService() {
241            return executorService;
242        }
243    
244        public void setExecutorService(ExecutorService executorService) {
245            this.executorService = executorService;
246        }
247    }