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.Queue;
020    import java.util.Timer;
021    
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    
025    /**
026     * Resequences elements based on a given {@link SequenceElementComparator}.
027     * This resequencer is designed for resequencing element streams. Resequenced
028     * elements are added to an output {@link Queue}. The resequencer is configured
029     * via the <code>timeout</code> and <code>capacity</code> properties.
030     * 
031     * <ul>
032     * <li><code>timeout</code>. Defines the timeout (in milliseconds) for a
033     * given element managed by this resequencer. An out-of-sequence element can
034     * only be marked as <i>ready-for-delivery</i> if it either times out or if it
035     * has an immediate predecessor (in that case it is in-sequence). If an
036     * immediate predecessor of a waiting element arrives the timeout task for the
037     * waiting element will be cancelled (which marks it as <i>ready-for-delivery</i>).
038     * <p>
039     * If the maximum out-of-sequence time between elements within a stream is
040     * known, the <code>timeout</code> value should be set to this value. In this
041     * case it is guaranteed that all elements of a stream will be delivered in
042     * sequence to the output queue. However, large <code>timeout</code> values
043     * might require a very high resequencer <code>capacity</code> which might be
044     * in conflict with available memory resources. The lower the
045     * <code>timeout</code> value is compared to the out-of-sequence time between
046     * elements within a stream the higher the probability is for out-of-sequence
047     * elements delivered by this resequencer.</li>
048     * <li><code>capacity</code>. The capacity of this resequencer.</li>
049     * </ul>
050     * 
051     * Whenever a timeout for a certain element occurs or an element has been added
052     * to this resequencer a delivery attempt is started. If a (sub)sequence of
053     * elements is <i>ready-for-delivery</i> then they are added to output queue.
054     * <p>
055     * The resequencer remembers the last-delivered element. If an element arrives
056     * which is the immediate successor of the last-delivered element it will be
057     * delivered immediately and the last-delivered element is adjusted accordingly.
058     * If the last-delivered element is <code>null</code> i.e. the resequencer was
059     * newly created the first arriving element will wait <code>timeout</code>
060     * milliseconds for being delivered to the output queue.
061     * 
062     * @author Martin Krasser
063     * 
064     * @version $Revision
065     */
066    public class ResequencerEngine<E> implements TimeoutHandler {
067    
068        private static final transient Log LOG = LogFactory.getLog(ResequencerEngine.class);
069        
070        private long timeout;    
071        private int capacity;    
072        private Queue<E> outQueue;    
073        private Element<E> lastDelivered;
074    
075        /**
076         * A sequence of elements for sorting purposes.
077         */
078        private Sequence<Element<E>> sequence;
079        
080        /**
081         * A timer for scheduling timeout notifications.
082         */
083        private Timer timer;
084        
085        /**
086         * Creates a new resequencer instance with a default timeout of 2000
087         * milliseconds. The capacity is set to {@link Integer#MAX_VALUE}.
088         * 
089         * @param comparator a sequence element comparator.
090         */
091        public ResequencerEngine(SequenceElementComparator<E> comparator) {
092            this(comparator, Integer.MAX_VALUE);
093        }
094    
095        /**
096         * Creates a new resequencer instance with a default timeout of 2000
097         * milliseconds.
098         * 
099         * @param comparator a sequence element comparator.
100         * @param capacity the capacity of this resequencer.
101         */
102        public ResequencerEngine(SequenceElementComparator<E> comparator, int capacity) {
103            this.timer = new Timer("Resequencer Timer");
104            this.sequence = createSequence(comparator);
105            this.capacity = capacity;
106            this.timeout = 2000L;
107            this.lastDelivered = null;
108        }
109        
110        /**
111         * Stops this resequencer (i.e. this resequencer's {@link Timer} instance).
112         */
113        public void stop() {
114            this.timer.cancel();
115        }
116        
117        /**
118         * Returns the output queue.
119         * 
120         * @return the output queue.
121         */
122        public Queue<E> getOutQueue() {
123            return outQueue;
124        }
125    
126        /**
127         * Sets the output queue.
128         * 
129         * @param outQueue output queue.
130         */
131        public void setOutQueue(Queue<E> outQueue) {
132            this.outQueue = outQueue;
133        }
134    
135        /**
136         * Returns this resequencer's timeout value.
137         * 
138         * @return the timeout in milliseconds.
139         */
140        public long getTimeout() {
141            return timeout;
142        }
143    
144        /**
145         * Sets this sequencer's timeout value.
146         * 
147         * @param timeout the timeout in milliseconds.
148         */
149        public void setTimeout(long timeout) {
150            this.timeout = timeout;
151        }
152    
153        /** 
154         * Handles a timeout notification by starting a delivery attempt.
155         * 
156         * @param timout timeout task that caused the notification.
157         */
158        public synchronized void timeout(Timeout timout) {
159            try {
160                while (deliver()) {
161                    // work done in deliver()
162                }
163            } catch (RuntimeException e) {
164                LOG.error("error during delivery", e);
165            }
166        }
167    
168        /**
169         * Adds an element to this resequencer throwing an exception if the maximum
170         * capacity is reached.
171         * 
172         * @param o element to be resequenced.
173         * @throws IllegalStateException if the element cannot be added at this time
174         *         due to capacity restrictions.
175         */
176        public synchronized void add(E o) {
177            if (sequence.size() >= capacity) {
178                throw new IllegalStateException("maximum capacity is reached");
179            }
180            insert(o);
181        }
182        
183        /**
184         * Adds an element to this resequencer waiting, if necessary, until capacity
185         * becomes available.
186         * 
187         * @param o element to be resequenced.
188         * @throws InterruptedException if interrupted while waiting.
189         */
190        public synchronized void put(E o) throws InterruptedException {
191            if (sequence.size() >= capacity) {
192                wait();
193            }
194            insert(o);
195        }
196        
197        /**
198         * Returns the last delivered element.
199         * 
200         * @return the last delivered element or <code>null</code> if no delivery
201         *         has been made yet.
202         */
203        E getLastDelivered() {
204            if (lastDelivered == null) {
205                return null;
206            }
207            return lastDelivered.getObject();
208        }
209        
210        /**
211         * Sets the last delivered element. This is for testing purposes only.
212         * 
213         * @param o an element.
214         */
215        void setLastDelivered(E o) {
216            lastDelivered = new Element<E>(o);
217        }
218        
219        /**
220         * Inserts the given element into this resequencing queue (sequence). If the
221         * element is not ready for immediate delivery and has no immediate
222         * presecessor then it is scheduled for timing out. After being timed out it
223         * is ready for delivery.
224         * 
225         * @param o an element.
226         */
227        private void insert(E o) {
228            // wrap object into internal element
229            Element<E> element = new Element<E>(o);
230            // add element to sequence in proper order
231            sequence.add(element);
232    
233            Element<E> successor = sequence.successor(element);
234            
235            // check if there is an immediate successor and cancel
236            // timer task (no need to wait any more for timeout)
237            if (successor != null) {
238                successor.cancel();
239            }
240            
241            // start delivery if current element is successor of last delivered element
242            if (successorOfLastDelivered(element)) {
243                // nothing to schedule
244            } else if (sequence.predecessor(element) != null) {
245                // nothing to schedule
246            } else {
247                Timeout t = defineTimeout();
248                element.schedule(t);
249            }
250            
251            // start delivery
252            while (deliver()) {
253                // work done in deliver()
254            }
255        }
256        
257        /**
258         * Attempts to deliver a single element from the head of the resequencer
259         * queue (sequence). Only elements which have not been scheduled for timing
260         * out or which already timed out can be delivered.
261         * 
262         * @return <code>true</code> if the element has been delivered
263         *         <code>false</code> otherwise.
264         */
265        private boolean deliver() {
266            if (sequence.size() == 0) {
267                return false;
268            }
269            // inspect element with lowest sequence value
270            Element<E> element = sequence.first();
271            
272            // if element is scheduled do not deliver and return
273            if (element.scheduled()) {
274                return false;
275            }
276            
277            // remove deliverable element from sequence
278            sequence.remove(element);
279    
280            // set the delivered element to last delivered element
281            lastDelivered = element;
282            
283            // notify a waiting thread that capacity is available
284            notify();
285            
286            // add element to output queue
287            outQueue.add(element.getObject());
288    
289            // element has been delivered
290            return true;
291        }
292        
293        /**
294         * Returns <code>true</code> if the given element is the immediate
295         * successor of the last delivered element.
296         * 
297         * @param element an element.
298         * @return <code>true</code> if the given element is the immediate
299         *         successor of the last delivered element.
300         */
301        private boolean successorOfLastDelivered(Element<E> element) {
302            if (lastDelivered == null) {
303                return false;
304            }
305            if (sequence.comparator().successor(element, lastDelivered)) {
306                return true;
307            }
308            return false;
309        }
310        
311        /**
312         * Creates a timeout task based on the timeout setting of this resequencer.
313         * 
314         * @return a new timeout task.
315         */
316        private Timeout defineTimeout() {
317            Timeout result = new Timeout(timer, timeout);
318            result.addTimeoutHandler(this);
319            return result;
320        }
321        
322        private static <E> Sequence<Element<E>> createSequence(SequenceElementComparator<E> comparator) {
323            return new Sequence<Element<E>>(new ElementComparator<E>(comparator));
324        }
325        
326    }