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