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.processor.resequencer;
018    
019    import java.util.Timer;
020    
021    /**
022     * Resequences elements based on a given {@link SequenceElementComparator}.
023     * This resequencer is designed for resequencing element streams. Stream-based
024     * resequencing has the advantage that the number of elements to be resequenced
025     * need not be known in advance. Resequenced elements are delivered via a
026     * {@link SequenceSender}.
027     * <p>
028     * The resequencer's behaviour for a given comparator is controlled by the
029     * <code>timeout</code> property. This is the timeout (in milliseconds) for a
030     * given element managed by this resequencer. An out-of-sequence element can
031     * only be marked as <i>ready-for-delivery</i> if it either times out or if it
032     * has an immediate predecessor (in that case it is in-sequence). If an
033     * immediate predecessor of a waiting element arrives the timeout task for the
034     * waiting element will be cancelled (which marks it as <i>ready-for-delivery</i>).
035     * <p>
036     * If the maximum out-of-sequence time difference between elements within a
037     * stream is known, the <code>timeout</code> value should be set to this
038     * value. In this case it is guaranteed that all elements of a stream will be
039     * delivered in sequence via the {@link SequenceSender}. The lower the
040     * <code>timeout</code> value is compared to the out-of-sequence time
041     * difference between elements within a stream the higher the probability is for
042     * out-of-sequence elements delivered by this resequencer. Delivery of elements
043     * must be explicitly triggered by applications using the {@link #deliver()} or
044     * {@link #deliverNext()} methods. Only elements that are <i>ready-for-delivery</i>
045     * are delivered by these methods. The longer an application waits to trigger a
046     * delivery the more elements may become <i>ready-for-delivery</i>.
047     * <p>
048     * The resequencer remembers the last-delivered element. If an element arrives
049     * which is the immediate successor of the last-delivered element it is
050     * <i>ready-for-delivery</i> immediately. After delivery the last-delivered
051     * element is adjusted accordingly. If the last-delivered element is
052     * <code>null</code> i.e. the resequencer was newly created the first arriving
053     * element needs <code>timeout</code> milliseconds in any case for becoming
054     * <i>ready-for-delivery</i>.
055     * <p>
056     *
057     * @version $Revision: 792319 $
058     */
059    public class ResequencerEngine<E> {
060    
061        /**
062         * The element that most recently hash been delivered or <code>null</code>
063         * if no element has been delivered yet.
064         */
065        private Element<E> lastDelivered;
066    
067        /**
068         * Minimum amount of time to wait for out-of-sequence elements.
069         */
070        private long timeout;
071    
072        /**
073         * A sequence of elements for sorting purposes.
074         */
075        private Sequence<Element<E>> sequence;
076    
077        /**
078         * A timer for scheduling timeout notifications.
079         */
080        private Timer timer;
081    
082        /**
083         * A strategy for sending sequence elements.
084         */
085        private SequenceSender<E> sequenceSender;
086    
087        /**
088         * Creates a new resequencer instance with a default timeout of 2000
089         * milliseconds.
090         *
091         * @param comparator a sequence element comparator.
092         */
093        public ResequencerEngine(SequenceElementComparator<E> comparator) {
094            this.sequence = createSequence(comparator);
095            this.timeout = 2000L;
096            this.lastDelivered = null;
097        }
098    
099        public void start() {
100            timer = new Timer("Camel Stream Resequencer Timer", true);
101        }
102    
103        /**
104         * Stops this resequencer (i.e. this resequencer's {@link Timer} instance).
105         */
106        public void stop() {
107            timer.cancel();
108        }
109    
110        /**
111         * Returns the number of elements currently maintained by this resequencer.
112         *
113         * @return the number of elements currently maintained by this resequencer.
114         */
115        public synchronized int size() {
116            return sequence.size();
117        }
118    
119        /**
120         * Returns this resequencer's timeout value.
121         *
122         * @return the timeout in milliseconds.
123         */
124        public long getTimeout() {
125            return timeout;
126        }
127    
128        /**
129         * Sets this sequencer's timeout value.
130         *
131         * @param timeout the timeout in milliseconds.
132         */
133        public void setTimeout(long timeout) {
134            this.timeout = timeout;
135        }
136    
137        /**
138         * Returns the sequence sender.
139         *
140         * @return the sequence sender.
141         */
142        public SequenceSender<E> getSequenceSender() {
143            return sequenceSender;
144        }
145    
146        /**
147         * Sets the sequence sender.
148         *
149         * @param sequenceSender a sequence element sender.
150         */
151        public void setSequenceSender(SequenceSender<E> sequenceSender) {
152            this.sequenceSender = sequenceSender;
153        }
154    
155        /**
156         * Returns the last delivered element.
157         *
158         * @return the last delivered element or <code>null</code> if no delivery
159         *         has been made yet.
160         */
161        E getLastDelivered() {
162            if (lastDelivered == null) {
163                return null;
164            }
165            return lastDelivered.getObject();
166        }
167    
168        /**
169         * Sets the last delivered element. This is for testing purposes only.
170         *
171         * @param o an element.
172         */
173        void setLastDelivered(E o) {
174            lastDelivered = new Element<E>(o);
175        }
176    
177        /**
178         * Inserts the given element into this resequencer. If the element is not
179         * ready for immediate delivery and has no immediate presecessor then it is
180         * scheduled for timing out. After being timed out it is ready for delivery.
181         *
182         * @param o an element.
183         */
184        public synchronized void insert(E o) {
185            // wrap object into internal element
186            Element<E> element = new Element<E>(o);
187            // add element to sequence in proper order
188            sequence.add(element);
189    
190            Element<E> successor = sequence.successor(element);
191    
192            // check if there is an immediate successor and cancel
193            // timer task (no need to wait any more for timeout)
194            if (successor != null) {
195                successor.cancel();
196            }
197    
198            // start delivery if current element is successor of last delivered element
199            if (successorOfLastDelivered(element)) {
200                // nothing to schedule
201            } else if (sequence.predecessor(element) != null) {
202                // nothing to schedule
203            } else {
204                element.schedule(defineTimeout());
205            }
206        }
207    
208        /**
209         * Delivers all elements which are currently ready to deliver.
210         *
211         * @throws Exception thrown by {@link SequenceSender#sendElement(Object)}.
212         *
213         * @see ResequencerEngine#deliverNext() 
214         */
215        public synchronized void deliver() throws Exception {
216            while (deliverNext()) {
217                // do nothing here
218            }
219        }
220    
221        /**
222         * Attempts to deliver a single element from the head of the resequencer
223         * queue (sequence). Only elements which have not been scheduled for timing
224         * out or which already timed out can be delivered. Elements are deliveref via
225         * {@link SequenceSender#sendElement(Object)}.
226         *
227         * @return <code>true</code> if the element has been delivered
228         *         <code>false</code> otherwise.
229         *
230         * @throws Exception thrown by {@link SequenceSender#sendElement(Object)}.
231         *
232         */
233        public boolean deliverNext() throws Exception {
234            if (sequence.size() == 0) {
235                return false;
236            }
237            // inspect element with lowest sequence value
238            Element<E> element = sequence.first();
239    
240            // if element is scheduled do not deliver and return
241            if (element.scheduled()) {
242                return false;
243            }
244    
245            // remove deliverable element from sequence
246            sequence.remove(element);
247    
248            // set the delivered element to last delivered element
249            lastDelivered = element;
250    
251            // deliver the sequence element
252            sequenceSender.sendElement(element.getObject());
253    
254            // element has been delivered
255            return true;
256        }
257    
258        /**
259         * Returns <code>true</code> if the given element is the immediate
260         * successor of the last delivered element.
261         *
262         * @param element an element.
263         * @return <code>true</code> if the given element is the immediate
264         *         successor of the last delivered element.
265         */
266        private boolean successorOfLastDelivered(Element<E> element) {
267            if (lastDelivered == null) {
268                return false;
269            }
270            if (sequence.comparator().successor(element, lastDelivered)) {
271                return true;
272            }
273            return false;
274        }
275    
276        /**
277         * Creates a timeout task based on the timeout setting of this resequencer.
278         *
279         * @return a new timeout task.
280         */
281        private Timeout defineTimeout() {
282            return new Timeout(timer, timeout);
283        }
284    
285        private static <E> Sequence<Element<E>> createSequence(SequenceElementComparator<E> comparator) {
286            return new Sequence<Element<E>>(new ElementComparator<E>(comparator));
287        }
288    
289    }