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