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 }