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.Collection;
021    import java.util.List;
022    
023    import javax.xml.bind.annotation.XmlElement;
024    import javax.xml.bind.annotation.XmlElementRef;
025    import javax.xml.bind.annotation.XmlRootElement;
026    import javax.xml.bind.annotation.XmlTransient;
027    
028    import org.apache.camel.Exchange;
029    import org.apache.camel.Expression;
030    import org.apache.camel.Processor;
031    import org.apache.camel.Route;
032    import org.apache.camel.model.config.BatchResequencerConfig;
033    import org.apache.camel.model.config.StreamResequencerConfig;
034    import org.apache.camel.model.language.ExpressionType;
035    import org.apache.camel.processor.Resequencer;
036    import org.apache.camel.processor.StreamResequencer;
037    import org.apache.camel.spi.RouteContext;
038    
039    /**
040     * Represents an XML <resequencer/> element
041     *
042     * @version $Revision: 43540 $
043     */
044    @XmlRootElement(name = "resequencer")
045    public class ResequencerType extends ProcessorType<ProcessorType> {
046        @XmlElementRef
047        private List<ExpressionType> expressions = new ArrayList<ExpressionType>();
048        @XmlElementRef
049        private List<ProcessorType<?>> outputs = new ArrayList<ProcessorType<?>>();
050        // Binding annotation at setter
051        private BatchResequencerConfig batchConfig;
052        // Binding annotation at setter
053        private StreamResequencerConfig streamConfig;
054        @XmlTransient
055        private List<Expression> expressionList;
056    
057        public ResequencerType() {
058            this(null);
059        }
060    
061        public ResequencerType(List<Expression> expressions) {
062            this.expressionList = expressions;
063            this.batch();
064        }
065    
066        @Override
067        public String getShortName() {
068            return "resequencer";
069        }
070    
071        /**
072         * Configures the stream-based resequencing algorithm using the default
073         * configuration.
074         *
075         * @return <code>this</code> instance.
076         */
077        public ResequencerType stream() {
078            return stream(StreamResequencerConfig.getDefault());
079        }
080    
081        /**
082         * Configures the batch-based resequencing algorithm using the default
083         * configuration.
084         *
085         * @return <code>this</code> instance.
086         */
087        public ResequencerType batch() {
088            return batch(BatchResequencerConfig.getDefault());
089        }
090    
091        /**
092         * Configures the stream-based resequencing algorithm using the given
093         * {@link StreamResequencerConfig}.
094         *
095         * @return <code>this</code> instance.
096         */
097        public ResequencerType stream(StreamResequencerConfig config) {
098            this.streamConfig = config;
099            this.batchConfig = null;
100            return this;
101        }
102    
103        /**
104         * Configures the batch-based resequencing algorithm using the given
105         * {@link BatchResequencerConfig}.
106         *
107         * @return <code>this</code> instance.
108         */
109        public ResequencerType batch(BatchResequencerConfig config) {
110            this.batchConfig = config;
111            this.streamConfig = null;
112            return this;
113        }
114    
115        public ResequencerType expression(ExpressionType expression) {
116            expressions.add(expression);
117            return this;
118        }
119    
120        @Override
121        public String toString() {
122            return "Resequencer[ " + getExpressions() + " -> " + getOutputs() + "]";
123        }
124    
125        @Override
126        public String getLabel() {
127            return ExpressionType.getLabel(getExpressions());
128        }
129    
130        public List<ExpressionType> getExpressions() {
131            return expressions;
132        }
133    
134        public List<ProcessorType<?>> getOutputs() {
135            return outputs;
136        }
137    
138        public void setOutputs(List<ProcessorType<?>> outputs) {
139            this.outputs = outputs;
140        }
141    
142        public BatchResequencerConfig getBatchConfig() {
143            return batchConfig;
144        }
145    
146        public BatchResequencerConfig getBatchConfig(BatchResequencerConfig defaultConfig) {
147            return batchConfig;
148        }
149    
150        public StreamResequencerConfig getStreamConfig() {
151            return streamConfig;
152        }
153    
154        @XmlElement(name = "batch-config", required = false)
155        public void setBatchConfig(BatchResequencerConfig batchConfig) {
156            // TODO: find out how to have these two within an <xsd:choice>
157            batch(batchConfig);
158        }
159    
160        @XmlElement(name = "stream-config", required = false)
161        public void setStreamConfig(StreamResequencerConfig streamConfig) {
162            // TODO: find out how to have these two within an <xsd:choice>
163            stream(streamConfig);
164        }
165    
166        @Override
167        public Processor createProcessor(RouteContext routeContext) throws Exception {
168            if (batchConfig != null) {
169                return createBatchResequencer(routeContext, batchConfig);
170            } else {
171                // streamConfig should be non-null if batchConfig is null
172                return createStreamResequencer(routeContext, streamConfig);
173            }
174        }
175    
176        @Override
177        public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
178            if (batchConfig != null) {
179                routes.add(createBatchResequencerRoute(routeContext));
180            } else {
181                // StreamResequencer created via createProcessor method
182                super.addRoutes(routeContext, routes);
183            }
184        }
185    
186        private Route<? extends Exchange> createBatchResequencerRoute(RouteContext routeContext) throws Exception {
187            final Resequencer resequencer = createBatchResequencer(routeContext, batchConfig);
188            return new Route(routeContext.getEndpoint(), resequencer) {
189                @Override
190                public String toString() {
191                    return "BatchResequencerRoute[" + getEndpoint() + " -> " + resequencer.getProcessor() + "]";
192                }
193            };
194        }
195    
196        protected Resequencer createBatchResequencer(RouteContext routeContext,
197                BatchResequencerConfig config) throws Exception {
198            Processor processor = routeContext.createProcessor(this);
199            Resequencer resequencer = new Resequencer(routeContext.getEndpoint(),
200                    processor, resolveExpressionList(routeContext));
201            resequencer.setBatchSize(config.getBatchSize());
202            resequencer.setBatchTimeout(config.getBatchTimeout());
203            return resequencer;
204        }
205    
206        protected StreamResequencer createStreamResequencer(RouteContext routeContext,
207                StreamResequencerConfig config) throws Exception {
208            config.getComparator().setExpressions(resolveExpressionList(routeContext));
209            Processor processor = routeContext.createProcessor(this);
210            StreamResequencer resequencer = new StreamResequencer(processor,
211                    config.getComparator(), config.getCapacity());
212            resequencer.setTimeout(config.getTimeout());
213            return resequencer;
214    
215        }
216    
217        private List<Expression> resolveExpressionList(RouteContext routeContext) {
218            if (expressionList == null) {
219                expressionList = new ArrayList<Expression>();
220                for (ExpressionType expression : expressions) {
221                    expressionList.add(expression.createExpression(routeContext));
222                }
223            }
224            if (expressionList.isEmpty()) {
225                throw new IllegalArgumentException("No expressions configured for: " + this);
226            }
227            return expressionList;
228        }
229    }