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 * <strong>Note:</strong> Instances of this class are not thread-safe.
057 * Resequencing should be done by calling {@link #insert(Object)} and
058 * {@link #deliver()} or {@link #deliverNext()} from a single thread.
059 *
060 * @author Martin Krasser
061 *
062 * @version $Revision: 52554 $
063 */
064 public class ResequencerEngine<E> {
065
066 /**
067 * The element that most recently hash been delivered or <code>null</code>
068 * if no element has been delivered yet.
069 */
070 private Element<E> lastDelivered;
071
072 /**
073 * Minimum amount of time to wait for out-of-sequence elements.
074 */
075 private long timeout;
076
077 /**
078 * A sequence of elements for sorting purposes.
079 */
080 private Sequence<Element<E>> sequence;
081
082 /**
083 * A timer for scheduling timeout notifications.
084 */
085 private Timer timer;
086
087 /**
088 * A strategy for sending sequence elements.
089 */
090 private SequenceSender<E> sequenceSender;
091
092 /**
093 * Creates a new resequencer instance with a default timeout of 2000
094 * milliseconds.
095 *
096 * @param comparator a sequence element comparator.
097 */
098 public ResequencerEngine(SequenceElementComparator<E> comparator) {
099 this.sequence = createSequence(comparator);
100 this.timeout = 2000L;
101 this.lastDelivered = null;
102 }
103
104 public void start() {
105 timer = new Timer("Stream Resequencer Timer");
106 }
107
108 /**
109 * Stops this resequencer (i.e. this resequencer's {@link Timer} instance).
110 */
111 public void stop() {
112 timer.cancel();
113 }
114
115 /**
116 * Returns the number of elements currently maintained by this resequencer.
117 *
118 * @return the number of elements currently maintained by this resequencer.
119 */
120 public int size() {
121 return sequence.size();
122 }
123
124 /**
125 * Returns this resequencer's timeout value.
126 *
127 * @return the timeout in milliseconds.
128 */
129 public long getTimeout() {
130 return timeout;
131 }
132
133 /**
134 * Sets this sequencer's timeout value.
135 *
136 * @param timeout the timeout in milliseconds.
137 */
138 public void setTimeout(long timeout) {
139 this.timeout = timeout;
140 }
141
142 /**
143 * Returns the sequence sender.
144 *
145 * @return the sequence sender.
146 */
147 public SequenceSender<E> getSequenceSender() {
148 return sequenceSender;
149 }
150
151 /**
152 * Sets the sequence sender.
153 *
154 * @param sequenceSender a sequence element sender.
155 */
156 public void setSequenceSender(SequenceSender<E> sequenceSender) {
157 this.sequenceSender = sequenceSender;
158 }
159
160 /**
161 * Returns the last delivered element.
162 *
163 * @return the last delivered element or <code>null</code> if no delivery
164 * has been made yet.
165 */
166 E getLastDelivered() {
167 if (lastDelivered == null) {
168 return null;
169 }
170 return lastDelivered.getObject();
171 }
172
173 /**
174 * Sets the last delivered element. This is for testing purposes only.
175 *
176 * @param o an element.
177 */
178 void setLastDelivered(E o) {
179 lastDelivered = new Element<E>(o);
180 }
181
182 /**
183 * Inserts the given element into this resequencer. If the element is not
184 * ready for immediate delivery and has no immediate presecessor then it is
185 * scheduled for timing out. After being timed out it is ready for delivery.
186 *
187 * @param o an element.
188 */
189 public void insert(E o) {
190 // wrap object into internal element
191 Element<E> element = new Element<E>(o);
192 // add element to sequence in proper order
193 sequence.add(element);
194
195 Element<E> successor = sequence.successor(element);
196
197 // check if there is an immediate successor and cancel
198 // timer task (no need to wait any more for timeout)
199 if (successor != null) {
200 successor.cancel();
201 }
202
203 // start delivery if current element is successor of last delivered element
204 if (successorOfLastDelivered(element)) {
205 // nothing to schedule
206 } else if (sequence.predecessor(element) != null) {
207 // nothing to schedule
208 } else {
209 element.schedule(defineTimeout());
210 }
211 }
212
213 /**
214 * Delivers all elements which are currently ready to deliver.
215 *
216 * @throws Exception thrown by {@link SequenceSender#sendElement(Object)}.
217 *
218 * @see ResequencerEngine#deliverNext()
219 */
220 public void deliver() throws Exception {
221 while (deliverNext()) {
222 // do nothing here
223 }
224 }
225
226 /**
227 * Attempts to deliver a single element from the head of the resequencer
228 * queue (sequence). Only elements which have not been scheduled for timing
229 * out or which already timed out can be delivered. Elements are deliveref via
230 * {@link SequenceSender#sendElement(Object)}.
231 *
232 * @return <code>true</code> if the element has been delivered
233 * <code>false</code> otherwise.
234 *
235 * @throws Exception thrown by {@link SequenceSender#sendElement(Object)}.
236 *
237 */
238 public boolean deliverNext() throws Exception {
239 if (sequence.size() == 0) {
240 return false;
241 }
242 // inspect element with lowest sequence value
243 Element<E> element = sequence.first();
244
245 // if element is scheduled do not deliver and return
246 if (element.scheduled()) {
247 return false;
248 }
249
250 // remove deliverable element from sequence
251 sequence.remove(element);
252
253 // set the delivered element to last delivered element
254 lastDelivered = element;
255
256 // deliver the sequence element
257 sequenceSender.sendElement(element.getObject());
258
259 // element has been delivered
260 return true;
261 }
262
263 /**
264 * Returns <code>true</code> if the given element is the immediate
265 * successor of the last delivered element.
266 *
267 * @param element an element.
268 * @return <code>true</code> if the given element is the immediate
269 * successor of the last delivered element.
270 */
271 private boolean successorOfLastDelivered(Element<E> element) {
272 if (lastDelivered == null) {
273 return false;
274 }
275 if (sequence.comparator().successor(element, lastDelivered)) {
276 return true;
277 }
278 return false;
279 }
280
281 /**
282 * Creates a timeout task based on the timeout setting of this resequencer.
283 *
284 * @return a new timeout task.
285 */
286 private Timeout defineTimeout() {
287 return new Timeout(timer, timeout);
288 }
289
290 private static <E> Sequence<Element<E>> createSequence(SequenceElementComparator<E> comparator) {
291 return new Sequence<Element<E>>(new ElementComparator<E>(comparator));
292 }
293
294 }