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 Log LOG = LogFactory.getLog(ResequencerEngine.class);
069        
070        private long timeout;
071        
072        private int capacity;
073        
074        private Queue<E> outQueue;
075        
076        private Element<E> lastDelivered;
077    
078        /**
079         * A sequence of elements for sorting purposes.
080         */
081        private Sequence<Element<E>> sequence;
082        
083        /**
084         * A timer for scheduling timeout notifications.
085         */
086        private Timer timer;
087        
088        /**
089         * Creates a new resequencer instance with a default timeout of 2000
090         * milliseconds. The capacity is set to {@link Integer#MAX_VALUE}.
091         * 
092         * @param comparator a sequence element comparator.
093         */
094        public ResequencerEngine(SequenceElementComparator<E> comparator) {
095            this(comparator, Integer.MAX_VALUE);
096        }
097    
098        /**
099         * Creates a new resequencer instance with a default timeout of 2000
100         * milliseconds.
101         * 
102         * @param comparator a sequence element comparator.
103         * @param capacity the capacity of this resequencer.
104         */
105        public ResequencerEngine(SequenceElementComparator<E> comparator, int capacity) {
106            this.timer = new Timer("Resequencer Timer");
107            this.sequence = createSequence(comparator);
108            this.capacity = capacity;
109            this.timeout = 2000L;
110            this.lastDelivered = null;
111        }
112        
113        /**
114         * Stops this resequencer (i.e. this resequencer's {@link Timer} instance).
115         */
116        public void stop() {
117            this.timer.cancel();
118        }
119        
120        /**
121         * Returns the output queue.
122         * 
123         * @return the output queue.
124         */
125        public Queue<E> getOutQueue() {
126            return outQueue;
127        }
128    
129        /**
130         * Sets the output queue.
131         * 
132         * @param outQueue output queue.
133         */
134        public void setOutQueue(Queue<E> outQueue) {
135            this.outQueue = outQueue;
136        }
137    
138        /**
139         * Returns this resequencer's timeout value.
140         * 
141         * @return the timeout in milliseconds.
142         */
143        public long getTimeout() {
144            return timeout;
145        }
146    
147        /**
148         * Sets this sequencer's timeout value.
149         * 
150         * @param timeout the timeout in milliseconds.
151         */
152        public void setTimeout(long timeout) {
153            this.timeout = timeout;
154        }
155    
156        /** 
157         * Handles a timeout notification by starting a delivery attempt.
158         * 
159         * @param timout timeout task that caused the notification.
160         */
161        public synchronized void timeout(Timeout timout) {
162            try {
163                while (deliver()) {
164                    // work done in deliver()
165                }
166            } catch (RuntimeException e) {
167                LOG.error("error during delivery", e);
168            }
169        }
170    
171        /**
172         * Adds an element to this resequencer throwing an exception if the maximum
173         * capacity is reached.
174         * 
175         * @param o element to be resequenced.
176         * @throws IllegalStateException if the element cannot be added at this time
177         *         due to capacity restrictions.
178         */
179        public synchronized void add(E o) {
180            if (sequence.size() >= capacity) {
181                throw new IllegalStateException("maximum capacity is reached");
182            }
183            insert(o);
184        }
185        
186        /**
187         * Adds an element to this resequencer waiting, if necessary, until capacity
188         * becomes available.
189         * 
190         * @param o element to be resequenced.
191         * @throws InterruptedException if interrupted while waiting.
192         */
193        public synchronized void put(E o) throws InterruptedException {
194            if (sequence.size() >= capacity) {
195                wait();
196            }
197            insert(o);
198        }
199        
200        /**
201         * Returns the last delivered element.
202         * 
203         * @return the last delivered element or <code>null</code> if no delivery
204         *         has been made yet.
205         */
206        E getLastDelivered() {
207            if (lastDelivered == null) {
208                return null;
209            }
210            return lastDelivered.getObject();
211        }
212        
213        /**
214         * Sets the last delivered element. This is for testing purposes only.
215         * 
216         * @param o an element.
217         */
218        void setLastDelivered(E o) {
219            lastDelivered = new Element<E>(o);
220        }
221        
222        /**
223         * Inserts the given element into this resequencing queue (sequence). If the
224         * element is not ready for immediate delivery and has no immediate
225         * presecessor then it is scheduled for timing out. After being timed out it
226         * is ready for delivery.
227         * 
228         * @param o an element.
229         */
230        private void insert(E o) {
231            // wrap object into internal element
232            Element<E> element = new Element<E>(o);
233            // add element to sequence in proper order
234            sequence.add(element);
235    
236            Element<E> successor = sequence.successor(element);
237            
238            // check if there is an immediate successor and cancel
239            // timer task (no need to wait any more for timeout)
240            if (successor != null) {
241                successor.cancel();
242            }
243            
244            // start delivery if current element is successor of last delivered element
245            if (successorOfLastDelivered(element)) {
246                // nothing to schedule
247            } else if (sequence.predecessor(element) != null) {
248                // nothing to schedule
249            } else {
250                Timeout t = defineTimeout();
251                element.schedule(t);
252            }
253            
254            // start delivery
255            while (deliver()) {
256                // work done in deliver()
257            }
258        }
259        
260        /**
261         * Attempts to deliver a single element from the head of the resequencer
262         * queue (sequence). Only elements which have not been scheduled for timing
263         * out or which already timed out can be delivered.
264         * 
265         * @return <code>true</code> if the element has been delivered
266         *         <code>false</code> otherwise.
267         */
268        private boolean deliver() {
269            if (sequence.size() == 0) {
270                return false;
271            }
272            // inspect element with lowest sequence value
273            Element<E> element = sequence.first();
274            
275            // if element is scheduled do not deliver and return
276            if (element.scheduled()) {
277                return false;
278            }
279            
280            // remove deliverable element from sequence
281            sequence.remove(element);
282    
283            // set the delivered element to last delivered element
284            lastDelivered = element;
285            
286            // notify a waiting thread that capacity is available
287            notify();
288            
289            // add element to output queue
290            outQueue.add(element.getObject());
291    
292            // element has been delivered
293            return true;
294        }
295        
296        /**
297         * Returns <code>true</code> if the given element is the immediate
298         * successor of the last delivered element.
299         * 
300         * @param element an element.
301         * @return <code>true</code> if the given element is the immediate
302         *         successor of the last delivered element.
303         */
304        private boolean successorOfLastDelivered(Element<E> element) {
305            if (lastDelivered == null) {
306                return false;
307            }
308            if (sequence.comparator().successor(element, lastDelivered)) {
309                return true;
310            }
311            return false;
312        }
313        
314        /**
315         * Creates a timeout task based on the timeout setting of this resequencer.
316         * 
317         * @return a new timeout task.
318         */
319        private Timeout defineTimeout() {
320            Timeout result = new Timeout(timer, timeout);
321            result.addTimeoutHandler(this);
322            return result;
323        }
324        
325        private static <E> Sequence<Element<E>> createSequence(SequenceElementComparator<E> comparator) {
326            return new Sequence<Element<E>>(new ElementComparator<E>(comparator));
327        }
328        
329    }