001 /**
002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003 * Licensed under the Apache License, Version 2.0 (the "License");
004 * you may not use this file except in compliance with the License.
005 * You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software
010 * distributed under the License is distributed on an "AS IS" BASIS,
011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 * See the License for the specific language governing permissions and
013 * limitations under the License. See accompanying LICENSE file.
014 */
015 package org.apache.oozie.util;
016
017 import java.util.AbstractQueue;
018 import java.util.ArrayList;
019 import java.util.Arrays;
020 import java.util.Collection;
021 import java.util.ConcurrentModificationException;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.concurrent.BlockingQueue;
025 import java.util.concurrent.DelayQueue;
026 import java.util.concurrent.Delayed;
027 import java.util.concurrent.TimeUnit;
028 import java.util.concurrent.atomic.AtomicInteger;
029 import java.util.concurrent.locks.ReentrantLock;
030
031 /**
032 * A Queue implementation that support queuing elements into the future and priority queuing.
033 * <p/>
034 * The {@link PriorityDelayQueue} avoids starvation by raising elements priority as they age.
035 * <p/>
036 * To support queuing elements into the future, the JDK <code>DelayQueue</code> is used.
037 * <p/>
038 * To support priority queuing, an array of <code>DelayQueue</code> sub-queues is used. Elements are consumed from the
039 * higher priority sub-queues first. From a sub-queue, elements are available based on their age.
040 * <p/>
041 * To avoid starvation, there is is maximum wait time for an an element in a sub-queue, after the maximum wait time has
042 * elapsed, the element is promoted to the next higher priority sub-queue. Eventually it will reach the maximum priority
043 * sub-queue and it will be consumed when it is the oldest element in the that sub-queue.
044 * <p/>
045 * Every time an element is promoted to a higher priority sub-queue, a new maximum wait time applies.
046 * <p/>
047 * This class does not use a separate thread for anti-starvation check, instead, the check is performed on polling and
048 * seeking operations. This check is performed, the most every 1/2 second.
049 */
050 public class PriorityDelayQueue<E> extends AbstractQueue<PriorityDelayQueue.QueueElement<E>>
051 implements BlockingQueue<PriorityDelayQueue.QueueElement<E>> {
052
053 /**
054 * Element wrapper required by the queue.
055 * <p/>
056 * This wrapper keeps track of the priority and the age of a queue element.
057 */
058 public static class QueueElement<E> implements Delayed {
059 private E element;
060 private int priority;
061 private long baseTime;
062 private boolean inQueue;
063
064 /**
065 * Create an Element wrapper.
066 *
067 * @param element element.
068 * @param priority priority of the element.
069 * @param delay delay of the element.
070 * @param unit time unit of the delay.
071 *
072 * @throws IllegalArgumentException if the element is <tt>NULL</tt>, the priority is negative or if the delay is
073 * negative.
074 */
075 public QueueElement(E element, int priority, long delay, TimeUnit unit) {
076 if (element == null) {
077 throw new IllegalArgumentException("element cannot be null");
078 }
079 if (priority < 0) {
080 throw new IllegalArgumentException("priority cannot be negative, [" + element + "]");
081 }
082 if (delay < 0) {
083 throw new IllegalArgumentException("delay cannot be negative");
084 }
085 this.element = element;
086 this.priority = priority;
087 setDelay(delay, unit);
088 }
089
090 /**
091 * Create an Element wrapper with no delay and minimum priority.
092 *
093 * @param element element.
094 */
095 public QueueElement(E element) {
096 this(element, 0, 0, TimeUnit.MILLISECONDS);
097 }
098
099 /**
100 * Return the element from the wrapper.
101 *
102 * @return the element.
103 */
104 public E getElement() {
105 return element;
106 }
107
108 /**
109 * Return the priority of the element.
110 *
111 * @return the priority of the element.
112 */
113 public int getPriority() {
114 return priority;
115 }
116
117 /**
118 * Set the delay of the element.
119 *
120 * @param delay delay of the element.
121 * @param unit time unit of the delay.
122 */
123 public void setDelay(long delay, TimeUnit unit) {
124 baseTime = System.currentTimeMillis() + unit.toMillis(delay);
125 }
126
127 /**
128 * Return the delay of the element.
129 *
130 * @param unit time unit of the delay.
131 *
132 * @return the delay in the specified time unit.
133 */
134 public long getDelay(TimeUnit unit) {
135 return unit.convert(baseTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
136 }
137
138 /**
139 * Compare the age of this wrapper element with another. The priority is not used for the comparision.
140 *
141 * @param o the other wrapper element to compare with.
142 *
143 * @return less than zero if this wrapper is older, zero if both wrapper elements have the same age, greater
144 * than zero if the parameter wrapper element is older.
145 */
146 public int compareTo(Delayed o) {
147 return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
148 }
149
150 /**
151 * Return the string representation of the wrapper element.
152 *
153 * @return the string representation of the wrapper element.
154 */
155 @Override
156 public String toString() {
157 StringBuilder sb = new StringBuilder();
158 sb.append("[").append(element).append("] priority=").append(priority).append(" delay=").
159 append(getDelay(TimeUnit.MILLISECONDS));
160 return sb.toString();
161 }
162
163 }
164
165 /**
166 * Frequency, in milliseconds, of the anti-starvation check.
167 */
168 public static final long ANTI_STARVATION_INTERVAL = 500;
169
170 private int priorities;
171 private DelayQueue<QueueElement<E>>[] queues;
172 private transient final ReentrantLock lock = new ReentrantLock();
173 private transient long lastAntiStarvationCheck = 0;
174 private long maxWait;
175 private int maxSize;
176 private AtomicInteger currentSize;
177
178 /**
179 * Create a <code>PriorityDelayQueue</code>.
180 *
181 * @param priorities number of priorities the queue will support.
182 * @param maxWait max wait time for elements before they are promoted to the next higher priority.
183 * @param unit time unit of the max wait time.
184 * @param maxSize maximum size of the queue, -1 means unbounded.
185 */
186 @SuppressWarnings("unchecked")
187 public PriorityDelayQueue(int priorities, long maxWait, TimeUnit unit, int maxSize) {
188 if (priorities < 1) {
189 throw new IllegalArgumentException("priorities must be 1 or more");
190 }
191 if (maxWait < 0) {
192 throw new IllegalArgumentException("maxWait must be greater than 0");
193 }
194 if (maxSize < -1 || maxSize == 0) {
195 throw new IllegalArgumentException("maxSize must be -1 or greater than 0");
196 }
197 this.priorities = priorities;
198 queues = new DelayQueue[priorities];
199 for (int i = 0; i < priorities; i++) {
200 queues[i] = new DelayQueue<QueueElement<E>>();
201 }
202 this.maxWait = unit.toMillis(maxWait);
203 this.maxSize = maxSize;
204 if (maxSize != -1) {
205 currentSize = new AtomicInteger();
206 }
207 }
208
209 /**
210 * Return number of priorities the queue supports.
211 *
212 * @return number of priorities the queue supports.
213 */
214 public int getPriorities() {
215 return priorities;
216 }
217
218 /**
219 * Return the max wait time for elements before they are promoted to the next higher priority.
220 *
221 * @param unit time unit of the max wait time.
222 *
223 * @return the max wait time in the specified time unit.
224 */
225 public long getMaxWait(TimeUnit unit) {
226 return unit.convert(maxWait, TimeUnit.MILLISECONDS);
227 }
228
229 /**
230 * Return the maximum queue size.
231 *
232 * @return the maximum queue size. If <code>-1</code> the queue is unbounded.
233 */
234 public long getMaxSize() {
235 return maxSize;
236 }
237
238 /**
239 * Return an iterator over all the {@link QueueElement} elements (both expired and unexpired) in this queue. The
240 * iterator does not return the elements in any particular order. The returned <tt>Iterator</tt> is a "weakly
241 * consistent" iterator that will never throw {@link ConcurrentModificationException}, and guarantees to traverse
242 * elements as they existed upon construction of the iterator, and may (but is not guaranteed to) reflect any
243 * modifications subsequent to construction.
244 *
245 * @return an iterator over the {@link QueueElement} elements in this queue.
246 */
247 @Override
248 @SuppressWarnings("unchecked")
249 public Iterator<QueueElement<E>> iterator() {
250 QueueElement[][] queueElements = new QueueElement[queues.length][];
251 try {
252 lock.lock();
253 for (int i = 0; i < queues.length; i++) {
254 queueElements[i] = queues[i].toArray(new QueueElement[0]);
255 }
256 }
257 finally {
258 lock.unlock();
259 }
260 List<QueueElement<E>> list = new ArrayList<QueueElement<E>>();
261 for (QueueElement[] elements : queueElements) {
262 list.addAll(Arrays.asList((QueueElement<E>[]) elements));
263 }
264 return list.iterator();
265 }
266
267 /**
268 * Return the number of elements in the queue.
269 *
270 * @return the number of elements in the queue.
271 */
272 @Override
273 public int size() {
274 int size = 0;
275 for (DelayQueue<QueueElement<E>> queue : queues) {
276 size += queue.size();
277 }
278 return size;
279 }
280
281 /**
282 * Return the number of elements on each priority sub-queue.
283 *
284 * @return the number of elements on each priority sub-queue.
285 */
286 public int[] sizes() {
287 int[] sizes = new int[queues.length];
288 for (int i = 0; i < queues.length; i++) {
289 sizes[i] = queues[i].size();
290 }
291 return sizes;
292 }
293
294 /**
295 * Inserts the specified element into this queue if it is possible to do
296 * so immediately without violating capacity restrictions, returning
297 * <tt>true</tt> upon success and throwing an
298 * <tt>IllegalStateException</tt> if no space is currently available.
299 * When using a capacity-restricted queue, it is generally preferable to
300 * use {@link #offer(Object) offer}.
301 *
302 * @param queueElement the {@link QueueElement} element to add.
303 * @return <tt>true</tt> (as specified by {@link Collection#add})
304 * @throws IllegalStateException if the element cannot be added at this
305 * time due to capacity restrictions
306 * @throws ClassCastException if the class of the specified element
307 * prevents it from being added to this queue
308 * @throws NullPointerException if the specified element is null
309 * @throws IllegalArgumentException if some property of the specified
310 * element prevents it from being added to this queue
311 */
312 @Override
313 public boolean add(QueueElement<E> queueElement) {
314 return offer(queueElement, false);
315 }
316
317 /**
318 * Insert the specified {@link QueueElement} element into the queue.
319 *
320 * @param queueElement the {@link QueueElement} element to add.
321 * @param ignoreSize if the queue is bound to a maximum size and the maximum size is reached, this parameter (if set
322 * to <tt>true</tt>) allows to ignore the maximum size and add the element to the queue.
323 *
324 * @return <tt>true</tt> if the element has been inserted, <tt>false</tt> if the element was not inserted (the queue
325 * has reached its maximum size).
326 *
327 * @throws NullPointerException if the specified element is null
328 */
329 boolean offer(QueueElement<E> queueElement, boolean ignoreSize) {
330 if (queueElement == null) {
331 throw new NullPointerException("queueElement is NULL");
332 }
333 if (queueElement.getPriority() < 0 && queueElement.getPriority() >= priorities) {
334 throw new IllegalArgumentException("priority out of range");
335 }
336 if (queueElement.inQueue) {
337 throw new IllegalStateException("queueElement already in a queue");
338 }
339 if (!ignoreSize && currentSize != null && currentSize.get() >= maxSize) {
340 return false;
341 }
342 boolean accepted = queues[queueElement.getPriority()].offer(queueElement);
343 debug("offer([{0}]), to P[{1}] delay[{2}ms] accepted[{3}]", queueElement.getElement().toString(),
344 queueElement.getPriority(), queueElement.getDelay(TimeUnit.MILLISECONDS), accepted);
345 if (accepted) {
346 if (currentSize != null) {
347 currentSize.incrementAndGet();
348 }
349 queueElement.inQueue = true;
350 }
351 return accepted;
352 }
353
354 /**
355 * Insert the specified element into the queue.
356 * <p/>
357 * The element is added with minimun priority and no delay.
358 *
359 * @param queueElement the element to add.
360 *
361 * @return <tt>true</tt> if the element has been inserted, <tt>false</tt> if the element was not inserted (the queue
362 * has reached its maximum size).
363 *
364 * @throws NullPointerException if the specified element is null
365 */
366 @Override
367 public boolean offer(QueueElement<E> queueElement) {
368 return offer(queueElement, false);
369 }
370
371 /**
372 * Retrieve and remove the head of this queue, or return <tt>null</tt> if this queue has no elements with an expired
373 * delay.
374 * <p/>
375 * The retrieved element is the oldest one from the highest priority sub-queue.
376 * <p/>
377 * Invocations to this method run the anti-starvation (once every interval check).
378 *
379 * @return the head of this queue, or <tt>null</tt> if this queue has no elements with an expired delay.
380 */
381 @Override
382 public QueueElement<E> poll() {
383 try {
384 lock.lock();
385 antiStarvation();
386 QueueElement<E> e = null;
387 int i = priorities;
388 for (; e == null && i > 0; i--) {
389 e = queues[i - 1].poll();
390 }
391 if (e != null) {
392 if (currentSize != null) {
393 currentSize.decrementAndGet();
394 }
395 e.inQueue = false;
396 debug("poll(): [{1}], from P[{2}]", e.getElement().toString(), i);
397 }
398 return e;
399 }
400 finally {
401 lock.unlock();
402 }
403 }
404
405 /**
406 * Retrieve, but does not remove, the head of this queue, or returns <tt>null</tt> if this queue is empty. Unlike
407 * <tt>poll</tt>, if no expired elements are available in the queue, this method returns the element that will
408 * expire next, if one exists.
409 *
410 * @return the head of this queue, or <tt>null</tt> if this queue is empty.
411 */
412 @Override
413 public QueueElement<E> peek() {
414 try {
415 lock.lock();
416 antiStarvation();
417 QueueElement<E> e = null;
418
419 QueueElement<E> [] seeks = new QueueElement[priorities];
420 boolean foundElement = false;
421 for (int i = priorities - 1; i > -1; i--) {
422 e = queues[i].peek();
423 debug("peek(): considering [{0}] from P[{1}]", e, i);
424 seeks[priorities - i - 1] = e;
425 foundElement |= e != null;
426 }
427 if (foundElement) {
428 e = null;
429 for (int i = 0; e == null && i < priorities; i++) {
430 if (seeks[i] != null && seeks[i].getDelay(TimeUnit.MILLISECONDS) > 0) {
431 debug("peek, ignoring [{0}]", seeks[i]);
432 }
433 else {
434 e = seeks[i];
435 }
436 }
437 if (e != null) {
438 debug("peek(): choosing [{0}]", e);
439 }
440 if (e == null) {
441 int first;
442 for (first = 0; e == null && first < priorities; first++) {
443 e = seeks[first];
444 }
445 if (e != null) {
446 debug("peek(): initial choosing [{0}]", e);
447 }
448 for (int i = first; i < priorities; i++) {
449 QueueElement<E> ee = seeks[i];
450 if (ee != null && ee.getDelay(TimeUnit.MILLISECONDS) < e.getDelay(TimeUnit.MILLISECONDS)) {
451 debug("peek(): choosing [{0}] over [{1}]", ee, e);
452 e = ee;
453 }
454 }
455 }
456 }
457 if (e != null) {
458 debug("peek(): [{0}], from P[{1}]", e.getElement().toString(), e.getPriority());
459 }
460 else {
461 debug("peek(): NULL");
462 }
463 return e;
464 }
465 finally {
466 lock.unlock();
467 }
468 }
469
470 /**
471 * Run the anti-starvation check every {@link #ANTI_STARVATION_INTERVAL} milliseconds.
472 * <p/>
473 * It promotes elements beyond max wait time to the next higher priority sub-queue.
474 */
475 private void antiStarvation() {
476 long now = System.currentTimeMillis();
477 if (now - lastAntiStarvationCheck > ANTI_STARVATION_INTERVAL) {
478 for (int i = 0; i < queues.length - 1; i++) {
479 antiStarvation(queues[i], queues[i + 1], "from P[" + i + "] to P[" + (i + 1) + "]");
480 }
481 StringBuilder sb = new StringBuilder();
482 for (int i = 0; i < queues.length; i++) {
483 sb.append("P[").append(i).append("]=").append(queues[i].size()).append(" ");
484 }
485 debug("sub-queue sizes: {0}", sb.toString());
486 lastAntiStarvationCheck = System.currentTimeMillis();
487 }
488 }
489
490 /**
491 * Promote elements beyond max wait time from a lower priority sub-queue to a higher priority sub-queue.
492 *
493 * @param lowerQ lower priority sub-queue.
494 * @param higherQ higher priority sub-queue.
495 * @param msg sub-queues msg (from-to) for debugging purposes.
496 */
497 private void antiStarvation(DelayQueue<QueueElement<E>> lowerQ, DelayQueue<QueueElement<E>> higherQ, String msg) {
498 int moved = 0;
499 QueueElement<E> e = lowerQ.poll();
500 while (e != null && e.getDelay(TimeUnit.MILLISECONDS) < -maxWait) {
501 e.setDelay(0, TimeUnit.MILLISECONDS);
502 if (!higherQ.offer(e)) {
503 throw new IllegalStateException("Could not move element to higher sub-queue, element rejected");
504 }
505 e.priority++;
506 e = lowerQ.poll();
507 moved++;
508 }
509 if (e != null) {
510 if (!lowerQ.offer(e)) {
511 throw new IllegalStateException("Could not reinsert element to current sub-queue, element rejected");
512 }
513 }
514 debug("anti-starvation, moved {0} element(s) {1}", moved, msg);
515 }
516
517 /**
518 * Method for debugging purposes. This implementation is a <tt>NOP</tt>.
519 * <p/>
520 * This method should be overriden for logging purposes.
521 * <p/>
522 * Message templates used by this class are in JDK's <tt>MessageFormat</tt> syntax.
523 *
524 * @param msgTemplate message template.
525 * @param msgArgs arguments for the message template.
526 */
527 protected void debug(String msgTemplate, Object... msgArgs) {
528 }
529
530 //BlockingQueue implementation
531
532 /**
533 * Insert the specified element into this queue, waiting if necessary
534 * for space to become available.
535 * <p/>
536 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
537 *
538 * @param e the element to add
539 * @throws InterruptedException if interrupted while waiting
540 * @throws ClassCastException if the class of the specified element
541 * prevents it from being added to this queue
542 * @throws NullPointerException if the specified element is null
543 * @throws IllegalArgumentException if some property of the specified
544 * element prevents it from being added to this queue
545 */
546 @Override
547 public void put(QueueElement<E> e) throws InterruptedException {
548 while (!offer(e, true)) {
549 Thread.sleep(10);
550 }
551 }
552
553 /**
554 * Insert the specified element into this queue, waiting up to the
555 * specified wait time if necessary for space to become available.
556 * <p/>
557 * IMPORTANT: This implementation forces the addition of the element to the queue regardless
558 * of the queue current size. The timeout value is ignored as the element is added immediately.
559 * <p/>
560 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
561 *
562 * @param e the element to add
563 * @param timeout how long to wait before giving up, in units of
564 * <tt>unit</tt>
565 * @param unit a <tt>TimeUnit</tt> determining how to interpret the
566 * <tt>timeout</tt> parameter
567 * @return <tt>true</tt> if successful, or <tt>false</tt> if
568 * the specified waiting time elapses before space is available
569 * @throws InterruptedException if interrupted while waiting
570 * @throws ClassCastException if the class of the specified element
571 * prevents it from being added to this queue
572 * @throws NullPointerException if the specified element is null
573 * @throws IllegalArgumentException if some property of the specified
574 * element prevents it from being added to this queue
575 */
576 @Override
577 public boolean offer(QueueElement<E> e, long timeout, TimeUnit unit) throws InterruptedException {
578 return offer(e, true);
579 }
580
581 /**
582 * Retrieve and removes the head of this queue, waiting if necessary
583 * until an element becomes available.
584 * <p/>
585 * IMPORTANT: This implementation has a delay of up to 10ms (when the queue is empty) to detect a new element
586 * is available. It is doing a 10ms sleep.
587 * <p/>
588 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
589 *
590 * @return the head of this queue
591 * @throws InterruptedException if interrupted while waiting
592 */
593 @Override
594 public QueueElement<E> take() throws InterruptedException {
595 QueueElement<E> e = poll();
596 while (e == null) {
597 Thread.sleep(10);
598 e = poll();
599 }
600 return e;
601 }
602
603 /**
604 * Retrieve and removes the head of this queue, waiting up to the
605 * specified wait time if necessary for an element to become available.
606 * <p/>
607 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
608 *
609 * @param timeout how long to wait before giving up, in units of
610 * <tt>unit</tt>
611 * @param unit a <tt>TimeUnit</tt> determining how to interpret the
612 * <tt>timeout</tt> parameter
613 * @return the head of this queue, or <tt>null</tt> if the
614 * specified waiting time elapses before an element is available
615 * @throws InterruptedException if interrupted while waiting
616 */
617 @Override
618 public QueueElement<E> poll(long timeout, TimeUnit unit) throws InterruptedException {
619 QueueElement<E> e = poll();
620 long time = System.currentTimeMillis() + unit.toMillis(timeout);
621 while (e == null && time > System.currentTimeMillis()) {
622 Thread.sleep(10);
623 e = poll();
624 }
625 return poll();
626 }
627
628 /**
629 * Return the number of additional elements that this queue can ideally
630 * (in the absence of memory or resource constraints) accept without
631 * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic
632 * limit.
633 *
634 * <p>Note that you <em>cannot</em> always tell if an attempt to insert
635 * an element will succeed by inspecting <tt>remainingCapacity</tt>
636 * because it may be the case that another thread is about to
637 * insert or remove an element.
638 * <p/>
639 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
640 *
641 * @return the remaining capacity
642 */
643 @Override
644 public int remainingCapacity() {
645 return (maxSize == -1) ? -1 : maxSize - size();
646 }
647
648 /**
649 * Remove all available elements from this queue and adds them
650 * to the given collection. This operation may be more
651 * efficient than repeatedly polling this queue. A failure
652 * encountered while attempting to add elements to
653 * collection <tt>c</tt> may result in elements being in neither,
654 * either or both collections when the associated exception is
655 * thrown. Attempt to drain a queue to itself result in
656 * <tt>IllegalArgumentException</tt>. Further, the behavior of
657 * this operation is undefined if the specified collection is
658 * modified while the operation is in progress.
659 * <p/>
660 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
661 *
662 * @param c the collection to transfer elements into
663 * @return the number of elements transferred
664 * @throws UnsupportedOperationException if addition of elements
665 * is not supported by the specified collection
666 * @throws ClassCastException if the class of an element of this queue
667 * prevents it from being added to the specified collection
668 * @throws NullPointerException if the specified collection is null
669 * @throws IllegalArgumentException if the specified collection is this
670 * queue, or some property of an element of this queue prevents
671 * it from being added to the specified collection
672 */
673 @Override
674 public int drainTo(Collection<? super QueueElement<E>> c) {
675 int count = 0;
676 for (DelayQueue<QueueElement<E>> q : queues) {
677 count += q.drainTo(c);
678 }
679 return count;
680 }
681
682 /**
683 * Remove at most the given number of available elements from
684 * this queue and adds them to the given collection. A failure
685 * encountered while attempting to add elements to
686 * collection <tt>c</tt> may result in elements being in neither,
687 * either or both collections when the associated exception is
688 * thrown. Attempt to drain a queue to itself result in
689 * <tt>IllegalArgumentException</tt>. Further, the behavior of
690 * this operation is undefined if the specified collection is
691 * modified while the operation is in progress.
692 * <p/>
693 * NOTE: This method is to fulfill the <tt>BlockingQueue<tt/> interface. Not implemented in the most optimal way.
694 *
695 * @param c the collection to transfer elements into
696 * @param maxElements the maximum number of elements to transfer
697 * @return the number of elements transferred
698 * @throws UnsupportedOperationException if addition of elements
699 * is not supported by the specified collection
700 * @throws ClassCastException if the class of an element of this queue
701 * prevents it from being added to the specified collection
702 * @throws NullPointerException if the specified collection is null
703 * @throws IllegalArgumentException if the specified collection is this
704 * queue, or some property of an element of this queue prevents
705 * it from being added to the specified collection
706 */
707 @Override
708 public int drainTo(Collection<? super QueueElement<E>> c, int maxElements) {
709 int left = maxElements;
710 int count = 0;
711 for (DelayQueue<QueueElement<E>> q : queues) {
712 int drained = q.drainTo(c, left);
713 count += drained;
714 left -= drained;
715 }
716 return count;
717 }
718
719 /**
720 * Removes all of the elements from this queue. The queue will be empty after this call returns.
721 */
722 @Override
723 public void clear() {
724 for (DelayQueue<QueueElement<E>> q : queues) {
725 q.clear();
726 }
727 }
728 }