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.ArrayList;
020import java.util.List;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlElement;
025import javax.xml.bind.annotation.XmlElementRef;
026import javax.xml.bind.annotation.XmlElements;
027import javax.xml.bind.annotation.XmlRootElement;
028import javax.xml.bind.annotation.XmlTransient;
029
030import org.apache.camel.Expression;
031import org.apache.camel.model.config.BatchResequencerConfig;
032import org.apache.camel.model.config.ResequencerConfig;
033import org.apache.camel.model.config.StreamResequencerConfig;
034import org.apache.camel.model.language.ExpressionDefinition;
035import org.apache.camel.processor.resequencer.ExpressionResultComparator;
036import org.apache.camel.spi.Metadata;
037
038/**
039 * Resequences (re-order) messages based on an expression
040 */
041@Metadata(label = "eip,routing")
042@XmlRootElement(name = "resequence")
043@XmlAccessorType(XmlAccessType.FIELD)
044public class ResequenceDefinition extends ProcessorDefinition<ResequenceDefinition> implements OutputNode {
045    @Metadata(required = false)
046    @XmlElements({@XmlElement(name = "batch-config", type = BatchResequencerConfig.class), @XmlElement(name = "stream-config", type = StreamResequencerConfig.class)})
047    private ResequencerConfig resequencerConfig;
048    @XmlTransient
049    private BatchResequencerConfig batchConfig;
050    @XmlTransient
051    private StreamResequencerConfig streamConfig;
052    @XmlElementRef
053    @Metadata(required = true)
054    private ExpressionDefinition expression;
055    @XmlElementRef
056    private List<ProcessorDefinition<?>> outputs = new ArrayList<>();
057
058    public ResequenceDefinition() {
059    }
060
061    public ResequenceDefinition(Expression expression) {
062        if (expression != null) {
063            setExpression(ExpressionNodeHelper.toExpressionDefinition(expression));
064        }
065    }
066
067    @Override
068    public List<ProcessorDefinition<?>> getOutputs() {
069        return outputs;
070    }
071
072    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
073        this.outputs = outputs;
074    }
075
076    // Fluent API
077    // -------------------------------------------------------------------------
078    /**
079     * Configures the stream-based resequencing algorithm using the default
080     * configuration.
081     *
082     * @return the builder
083     */
084    public ResequenceDefinition stream() {
085        return stream(StreamResequencerConfig.getDefault());
086    }
087
088    /**
089     * Configures the batch-based resequencing algorithm using the default
090     * configuration.
091     *
092     * @return the builder
093     */
094    public ResequenceDefinition batch() {
095        return batch(BatchResequencerConfig.getDefault());
096    }
097
098    /**
099     * Configures the stream-based resequencing algorithm using the given
100     * {@link StreamResequencerConfig}.
101     *
102     * @param config the config
103     * @return the builder
104     */
105    public ResequenceDefinition stream(StreamResequencerConfig config) {
106        this.streamConfig = config;
107        this.batchConfig = null;
108        return this;
109    }
110
111    /**
112     * Configures the batch-based resequencing algorithm using the given
113     * {@link BatchResequencerConfig}.
114     *
115     * @param config the config
116     * @return the builder
117     */
118    public ResequenceDefinition batch(BatchResequencerConfig config) {
119        this.batchConfig = config;
120        this.streamConfig = null;
121        return this;
122    }
123
124    /**
125     * Sets the timeout
126     * 
127     * @param timeout timeout in millis
128     * @return the builder
129     */
130    public ResequenceDefinition timeout(long timeout) {
131        if (streamConfig != null) {
132            streamConfig.setTimeout(timeout);
133        } else {
134            // initialize batch mode as its default mode
135            if (batchConfig == null) {
136                batch();
137            }
138            batchConfig.setBatchTimeout(timeout);
139        }
140        return this;
141    }
142
143    /**
144     * Sets the interval in milli seconds the stream resequencer will at most
145     * wait while waiting for condition of being able to deliver.
146     *
147     * @param deliveryAttemptInterval interval in millis
148     * @return the builder
149     */
150    public ResequenceDefinition deliveryAttemptInterval(long deliveryAttemptInterval) {
151        if (streamConfig == null) {
152            throw new IllegalStateException("deliveryAttemptInterval() only supported for stream resequencer");
153        }
154        streamConfig.setDeliveryAttemptInterval(deliveryAttemptInterval);
155        return this;
156    }
157
158    /**
159     * Sets the rejectOld flag to throw an error when a message older than the
160     * last delivered message is processed
161     * 
162     * @return the builder
163     */
164    public ResequenceDefinition rejectOld() {
165        if (streamConfig == null) {
166            throw new IllegalStateException("rejectOld() only supported for stream resequencer");
167        }
168        streamConfig.setRejectOld(true);
169        return this;
170    }
171
172    /**
173     * Sets the in batch size for number of exchanges received
174     * 
175     * @param batchSize the batch size
176     * @return the builder
177     */
178    public ResequenceDefinition size(int batchSize) {
179        if (streamConfig != null) {
180            throw new IllegalStateException("size() only supported for batch resequencer");
181        }
182        // initialize batch mode as its default mode
183        if (batchConfig == null) {
184            batch();
185        }
186        batchConfig.setBatchSize(batchSize);
187        return this;
188    }
189
190    /**
191     * Sets the capacity for the stream resequencer
192     *
193     * @param capacity the capacity
194     * @return the builder
195     */
196    public ResequenceDefinition capacity(int capacity) {
197        if (streamConfig == null) {
198            throw new IllegalStateException("capacity() only supported for stream resequencer");
199        }
200        streamConfig.setCapacity(capacity);
201        return this;
202
203    }
204
205    /**
206     * Enables duplicates for the batch resequencer mode
207     * 
208     * @return the builder
209     */
210    public ResequenceDefinition allowDuplicates() {
211        if (streamConfig != null) {
212            throw new IllegalStateException("allowDuplicates() only supported for batch resequencer");
213        }
214        // initialize batch mode as its default mode
215        if (batchConfig == null) {
216            batch();
217        }
218        batchConfig.setAllowDuplicates(true);
219        return this;
220    }
221
222    /**
223     * Enables reverse mode for the batch resequencer mode.
224     * <p/>
225     * This means the expression for determine the sequence order will be
226     * reversed. Can be used for Z..A or 9..0 ordering.
227     *
228     * @return the builder
229     */
230    public ResequenceDefinition reverse() {
231        if (streamConfig != null) {
232            throw new IllegalStateException("reverse() only supported for batch resequencer");
233        }
234        // initialize batch mode as its default mode
235        if (batchConfig == null) {
236            batch();
237        }
238        batchConfig.setReverse(true);
239        return this;
240    }
241
242    /**
243     * If an incoming {@link org.apache.camel.Exchange} is invalid, then it will
244     * be ignored.
245     *
246     * @return builder
247     */
248    public ResequenceDefinition ignoreInvalidExchanges() {
249        if (streamConfig != null) {
250            streamConfig.setIgnoreInvalidExchanges(true);
251        } else {
252            // initialize batch mode as its default mode
253            if (batchConfig == null) {
254                batch();
255            }
256            batchConfig.setIgnoreInvalidExchanges(true);
257        }
258        return this;
259    }
260
261    /**
262     * Sets the comparator to use for stream resequencer
263     *
264     * @param comparator the comparator
265     * @return the builder
266     */
267    public ResequenceDefinition comparator(ExpressionResultComparator comparator) {
268        if (streamConfig == null) {
269            throw new IllegalStateException("comparator() only supported for stream resequencer");
270        }
271        streamConfig.setComparator(comparator);
272        return this;
273    }
274
275    @Override
276    public String toString() {
277        return "Resequencer[" + getExpression() + " -> " + getOutputs() + "]";
278    }
279
280    @Override
281    public String getShortName() {
282        return "resequence";
283    }
284
285    @Override
286    public String getLabel() {
287        return "resequencer[" + (getExpression() != null ? getExpression().getLabel() : "") + "]";
288    }
289
290    public ResequencerConfig getResequencerConfig() {
291        return resequencerConfig;
292    }
293
294    /**
295     * To configure the resequencer in using either batch or stream
296     * configuration. Will by default use batch configuration.
297     */
298    public void setResequencerConfig(ResequencerConfig resequencerConfig) {
299        this.resequencerConfig = resequencerConfig;
300    }
301
302    public BatchResequencerConfig getBatchConfig() {
303        if (batchConfig == null && resequencerConfig != null && resequencerConfig instanceof BatchResequencerConfig) {
304            return (BatchResequencerConfig)resequencerConfig;
305        }
306        return batchConfig;
307    }
308
309    public StreamResequencerConfig getStreamConfig() {
310        if (streamConfig == null && resequencerConfig != null && resequencerConfig instanceof StreamResequencerConfig) {
311            return (StreamResequencerConfig)resequencerConfig;
312        }
313        return streamConfig;
314    }
315
316    public void setBatchConfig(BatchResequencerConfig batchConfig) {
317        this.batchConfig = batchConfig;
318    }
319
320    public void setStreamConfig(StreamResequencerConfig streamConfig) {
321        this.streamConfig = streamConfig;
322    }
323
324    public ExpressionDefinition getExpression() {
325        return expression;
326    }
327
328    /**
329     * Expression to use for re-ordering the messages, such as a header with a
330     * sequence number
331     */
332    public void setExpression(ExpressionDefinition expression) {
333        this.expression = expression;
334    }
335
336    /**
337     * Expression to use for re-ordering the messages, such as a header with a
338     * sequence number
339     */
340    public void setExpression(Expression expression) {
341        setExpression(new ExpressionDefinition(expression));
342    }
343
344}