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    
022    import javax.xml.bind.annotation.XmlElement;
023    import javax.xml.bind.annotation.XmlElementRef;
024    import javax.xml.bind.annotation.XmlRootElement;
025    import javax.xml.bind.annotation.XmlTransient;
026    
027    import org.apache.camel.Expression;
028    import org.apache.camel.Processor;
029    import org.apache.camel.model.config.BatchResequencerConfig;
030    import org.apache.camel.model.config.StreamResequencerConfig;
031    import org.apache.camel.model.language.ExpressionDefinition;
032    import org.apache.camel.processor.Resequencer;
033    import org.apache.camel.processor.StreamResequencer;
034    import org.apache.camel.processor.resequencer.ExpressionResultComparator;
035    import org.apache.camel.spi.RouteContext;
036    
037    /**
038     * Represents an XML <resequence/> element
039     *
040     * @version $Revision: 751373 $
041     */
042    @XmlRootElement(name = "resequence")
043    public class ResequenceDefinition extends ProcessorDefinition<ProcessorDefinition> {
044        @XmlElementRef
045        private List<ExpressionDefinition> expressions = new ArrayList<ExpressionDefinition>();
046        @XmlElementRef
047        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
048        // Binding annotation at setter
049        private BatchResequencerConfig batchConfig;
050        // Binding annotation at setter
051        private StreamResequencerConfig streamConfig;
052        @XmlTransient
053        private List<Expression> expressionList;
054    
055        public ResequenceDefinition() {
056            this(null);
057        }
058    
059        public ResequenceDefinition(List<Expression> expressions) {
060            this.expressionList = expressions;
061            this.batch();
062        }
063    
064        @Override
065        public String getShortName() {
066            return "resequence";
067        }
068        
069        // Fluent API
070        // -------------------------------------------------------------------------
071        /**
072         * Configures the stream-based resequencing algorithm using the default
073         * configuration.
074         *
075         * @return the builder
076         */
077        public ResequenceDefinition stream() {
078            return stream(StreamResequencerConfig.getDefault());
079        }
080    
081        /**
082         * Configures the batch-based resequencing algorithm using the default
083         * configuration.
084         *
085         * @return the builder
086         */
087        public ResequenceDefinition batch() {
088            return batch(BatchResequencerConfig.getDefault());
089        }
090    
091        /**
092         * Configures the stream-based resequencing algorithm using the given
093         * {@link StreamResequencerConfig}.
094         *
095         * @param config  the config
096         * @return the builder
097         */
098        public ResequenceDefinition stream(StreamResequencerConfig config) {
099            this.streamConfig = config;
100            this.batchConfig = null;
101            return this;
102        }
103    
104        /**
105         * Configures the batch-based resequencing algorithm using the given
106         * {@link BatchResequencerConfig}.
107         *
108         * @param config  the config
109         * @return the builder
110         */
111        public ResequenceDefinition batch(BatchResequencerConfig config) {
112            this.batchConfig = config;
113            this.streamConfig = null;
114            return this;
115        }
116    
117        /**
118         * Sets the expression to use for reordering
119         *
120         * @param expression  the expression
121         * @return the builder
122         */
123        public ResequenceDefinition expression(ExpressionDefinition expression) {
124            expressions.add(expression);
125            return this;
126        }
127    
128        /**
129         * Sets the timeout
130         * @param timeout  timeout in millis
131         * @return the builder
132         */
133        public ResequenceDefinition timeout(long timeout) {
134            if (batchConfig != null) {
135                batchConfig.setBatchTimeout(timeout);
136            } else {
137                streamConfig.setTimeout(timeout);
138            }
139            return this;
140        }
141    
142        /**
143         * Sets the in batch size for number of exchanges received
144         * @param batchSize  the batch size
145         * @return the builder
146         */
147        public ResequenceDefinition size(int batchSize) {
148            if (batchConfig == null) {
149                throw new IllegalStateException("size() only supported for batch resequencer");
150            }
151            batchConfig.setBatchSize(batchSize);
152            return this;
153        }
154    
155        /**
156         * Sets the capacity for the stream resequencer
157         *
158         * @param capacity  the capacity
159         * @return the builder
160         */
161        public ResequenceDefinition capacity(int capacity) {
162            if (streamConfig == null) {
163                throw new IllegalStateException("capacity() only supported for stream resequencer");
164            }
165            streamConfig.setCapacity(capacity);
166            return this;
167    
168        }
169    
170        /**
171         * Sets the comparator to use for stream resequencer
172         *
173         * @param comparator  the comparator
174         * @return the builder
175         */
176        public ResequenceDefinition comparator(ExpressionResultComparator comparator) {
177            if (streamConfig == null) {
178                throw new IllegalStateException("comparator() only supported for stream resequencer");
179            }
180            streamConfig.setComparator(comparator);
181            return this;
182        }
183    
184        @Override
185        public String toString() {
186            return "Resequencer[" + getExpressions() + " -> " + getOutputs() + "]";
187        }
188    
189        @Override
190        public String getLabel() {
191            return ExpressionDefinition.getLabel(getExpressions());
192        }
193    
194        public List<ExpressionDefinition> getExpressions() {
195            return expressions;
196        }
197    
198        public List<ProcessorDefinition> getOutputs() {
199            return outputs;
200        }
201    
202        public void setOutputs(List<ProcessorDefinition> outputs) {
203            this.outputs = outputs;
204        }
205    
206        public BatchResequencerConfig getBatchConfig() {
207            return batchConfig;
208        }
209    
210        public BatchResequencerConfig getBatchConfig(BatchResequencerConfig defaultConfig) {
211            return batchConfig;
212        }
213    
214        public StreamResequencerConfig getStreamConfig() {
215            return streamConfig;
216        }
217    
218        @XmlElement(name = "batch-config", required = false)
219        public void setBatchConfig(BatchResequencerConfig batchConfig) {
220            batch(batchConfig);
221        }
222    
223        @XmlElement(name = "stream-config", required = false)
224        public void setStreamConfig(StreamResequencerConfig streamConfig) {
225            stream(streamConfig);
226        }
227    
228        @Override
229        public Processor createProcessor(RouteContext routeContext) throws Exception {
230            if (batchConfig != null) {
231                return createBatchResequencer(routeContext, batchConfig);
232            } else {
233                // streamConfig should be non-null if batchConfig is null
234                return createStreamResequencer(routeContext, streamConfig);
235            }
236        }
237    
238        /**
239         * Creates a batch {@link Resequencer} instance applying the given
240         * <code>config</code>.
241         * 
242         * @param routeContext route context.
243         * @param config batch resequencer configuration.
244         * @return the configured batch resequencer.
245         * @throws Exception can be thrown
246         */
247        protected Resequencer createBatchResequencer(RouteContext routeContext,
248                BatchResequencerConfig config) throws Exception {
249            Processor processor = routeContext.createProcessor(this);
250            Resequencer resequencer = new Resequencer(processor, resolveExpressionList(routeContext));
251            resequencer.setBatchSize(config.getBatchSize());
252            resequencer.setBatchTimeout(config.getBatchTimeout());
253            return resequencer;
254        }
255    
256        /**
257         * Creates a {@link StreamResequencer} instance applying the given
258         * <code>config</code>.
259         * 
260         * @param routeContext route context.
261         * @param config stream resequencer configuration.
262         * @return the configured stream resequencer.
263         * @throws Exception can be thrwon
264         */
265        protected StreamResequencer createStreamResequencer(RouteContext routeContext, 
266                StreamResequencerConfig config) throws Exception {
267            config.getComparator().setExpressions(resolveExpressionList(routeContext));
268            Processor processor = routeContext.createProcessor(this);
269            StreamResequencer resequencer = new StreamResequencer(processor, config.getComparator());
270            resequencer.setTimeout(config.getTimeout());
271            resequencer.setCapacity(config.getCapacity());
272            return resequencer;
273            
274        }
275    
276        private List<Expression> resolveExpressionList(RouteContext routeContext) {
277            if (expressionList == null) {
278                expressionList = new ArrayList<Expression>();
279                for (ExpressionDefinition expression : expressions) {
280                    expressionList.add(expression.createExpression(routeContext));
281                }
282            }
283            if (expressionList.isEmpty()) {
284                throw new IllegalArgumentException("No expressions configured for: " + this);
285            }
286            return expressionList;
287        }
288    }