001package com.github.theholywaffle.teamspeak3.api; 002 003/* 004 * #%L 005 * TeamSpeak 3 Java API 006 * %% 007 * Copyright (C) 2014 Bert De Geyter 008 * %% 009 * Permission is hereby granted, free of charge, to any person obtaining a copy 010 * of this software and associated documentation files (the "Software"), to deal 011 * in the Software without restriction, including without limitation the rights 012 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 013 * copies of the Software, and to permit persons to whom the Software is 014 * furnished to do so, subject to the following conditions: 015 * 016 * The above copyright notice and this permission notice shall be included in 017 * all copies or substantial portions of the Software. 018 * 019 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 020 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 021 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 022 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 023 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 024 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 025 * THE SOFTWARE. 026 * #L% 027 */ 028 029import com.github.theholywaffle.teamspeak3.TS3ApiAsync; 030import com.github.theholywaffle.teamspeak3.api.exception.TS3Exception; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import java.util.Arrays; 035import java.util.Collection; 036import java.util.Iterator; 037import java.util.List; 038import java.util.concurrent.CancellationException; 039import java.util.concurrent.Future; 040import java.util.concurrent.TimeUnit; 041import java.util.concurrent.TimeoutException; 042import java.util.concurrent.atomic.AtomicInteger; 043 044/** 045 * Represents the result of an asynchronous execution of a query command. 046 * <p> 047 * Basically, this class is a container for a server response which will 048 * arrive at some time in the future. It also accounts for the possibility 049 * that a command might fail and that a future might be cancelled by a user. 050 * </p> 051 * A {@code CommandFuture} can therefore have 4 different states: 052 * <ul> 053 * <li><b>Waiting</b> - No response from the server has arrived yet</li> 054 * <li><b>Cancelled</b> - A user cancelled this future before a response from the server could arrive</li> 055 * <li><b>Failed</b> - The server received the command but responded with an error message</li> 056 * <li><b>Succeeded</b> - The server successfully processed the command and sent back a result</li> 057 * </ul> 058 * You can check the state of the future using the methods {@link #isDone()}, 059 * {@link #isSuccessful()}, {@link #hasFailed()} and {@link #isCancelled()}. 060 * <p> 061 * A {@code CommandFuture}'s value can be retrieved by calling {@link #get()} 062 * or {@link #get(long, TimeUnit)}, which block the current thread until the 063 * server response arrives. The method with a timeout should be preferred 064 * as there's no guarantee that a proper response (or an error message) 065 * will ever arrive, e.g. in case of a permanent disconnect. 066 * There are also variations of these methods which ignore thread interrupts, 067 * {@link #getUninterruptibly()} and {@link #getUninterruptibly(long, TimeUnit)}. 068 * </p><p> 069 * Note that <b>these methods</b> all wait for the response to arrive and thereby 070 * <b>revert to synchronous</b> execution. If you want to handle the server response 071 * asynchronously, you need to register success and failure listeners. 072 * These listeners will be called in a separate thread once a response arrives. 073 * </p><p> 074 * Each {@code CommandFuture} can only ever have one {@link SuccessListener} and 075 * one {@link FailureListener} registered. All {@link TS3ApiAsync} methods are 076 * guaranteed to return a {@code CommandFuture} with no listeners registered. 077 * </p><p> 078 * To set the value of a {@code CommandFuture}, the {@link #set(Object)} method is used; 079 * to notify it of a failure, {@link #fail(TS3Exception)} is used. You usually 080 * shouldn't call these methods yourself, however. That's the job of the API. 081 * </p><p> 082 * {@code CommandFuture}s are thread-safe. All state-changing methods are synchronized. 083 * </p> 084 * 085 * @param <V> 086 * the type of the value 087 * 088 * @see TS3ApiAsync 089 */ 090public class CommandFuture<V> implements Future<V> { 091 092 private static final Logger log = LoggerFactory.getLogger(CommandFuture.class); 093 094 private enum FutureState { 095 WAITING, 096 CANCELLED, 097 FAILED, 098 SUCCEEDED 099 } 100 101 /** 102 * Just a plain object used for its monitor to synchronize access to the 103 * critical sections of this future and to signal state changes to any 104 * threads waiting in {@link #get()} and {@link #getUninterruptibly()} methods. 105 */ 106 private final Object monitor = new Object(); 107 108 /** 109 * The current state of the future. Marked as volatile so {@link #isDone()} 110 * and similar functions can work without synchronization. 111 * State transitions and check-then-acts must be guarded by monitor. 112 */ 113 private volatile FutureState state = FutureState.WAITING; 114 115 // All guarded by monitor 116 private V value = null; 117 private TS3Exception exception = null; 118 private SuccessListener<? super V> successListener = null; 119 private FailureListener failureListener = null; 120 121 /** 122 * Waits indefinitely until the command completes. 123 * <p> 124 * If the thread is interrupted while waiting for the command 125 * to complete, this method will throw an {@code InterruptedException} 126 * and the thread's interrupt flag will be cleared. 127 * </p><p><i> 128 * Please note that this method is blocking and thus negates 129 * the advantage of the asynchronous nature of this class. 130 * Consider using {@link #onSuccess(SuccessListener)} and 131 * {@link #onFailure(FailureListener)} instead. 132 * </i></p> 133 * 134 * @throws InterruptedException 135 * if the method is interrupted by calling {@link Thread#interrupt()}. 136 * The interrupt flag will be cleared 137 */ 138 public void await() throws InterruptedException { 139 synchronized (monitor) { 140 while (state == FutureState.WAITING) { 141 monitor.wait(); 142 } 143 } 144 } 145 146 /** 147 * Waits for at most the given time until the command completes. 148 * <p> 149 * If the thread is interrupted while waiting for the command 150 * to complete, this method will throw an {@code InterruptedException} 151 * and the thread's interrupt flag will be cleared. 152 * </p><p><i> 153 * Please note that this method is blocking and thus negates 154 * the advantage of the asynchronous nature of this class. 155 * Consider using {@link #onSuccess(SuccessListener)} and 156 * {@link #onFailure(FailureListener)} instead. 157 * </i></p> 158 * 159 * @param timeout 160 * the maximum amount of the given time unit to wait 161 * @param unit 162 * the time unit of the timeout argument 163 * 164 * @throws InterruptedException 165 * if the method is interrupted by calling {@link Thread#interrupt()}. 166 * The interrupt flag will be cleared 167 * @throws TimeoutException 168 * if the given time elapsed without the command completing 169 */ 170 public void await(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { 171 synchronized (monitor) { 172 final long end = System.currentTimeMillis() + unit.toMillis(timeout); 173 while (state == FutureState.WAITING && System.currentTimeMillis() < end) { 174 monitor.wait(end - System.currentTimeMillis()); 175 } 176 177 if (state == FutureState.WAITING) throw new TimeoutException(); 178 } 179 } 180 181 /** 182 * Waits indefinitely until the command completes. 183 * <p> 184 * If the thread is interrupted while waiting for the command 185 * to complete, the interrupt is simply ignored and no 186 * {@link InterruptedException} is thrown. 187 * </p><p><i> 188 * Please note that this method is blocking and thus negates 189 * the advantage of the asynchronous nature of this class. 190 * Consider using {@link #onSuccess(SuccessListener)} and 191 * {@link #onFailure(FailureListener)} instead. 192 * </i></p> 193 */ 194 public void awaitUninterruptibly() { 195 synchronized (monitor) { 196 boolean interrupted = false; 197 while (state == FutureState.WAITING) { 198 try { 199 monitor.wait(); 200 } catch (InterruptedException e) { 201 interrupted = true; 202 } 203 } 204 205 if (interrupted) { 206 // Restore the interrupt for the caller 207 Thread.currentThread().interrupt(); 208 } 209 } 210 } 211 212 /** 213 * Waits for at most the given time until the command completes. 214 * <p> 215 * If the thread is interrupted while waiting for the command 216 * to complete, the interrupt is simply ignored and no 217 * {@link InterruptedException} is thrown. 218 * </p><p><i> 219 * Please note that this method is blocking and thus negates 220 * the advantage of the asynchronous nature of this class. 221 * Consider using {@link #onSuccess(SuccessListener)} and 222 * {@link #onFailure(FailureListener)} instead. 223 * </i></p> 224 * 225 * @param timeout 226 * the maximum amount of the given time unit to wait 227 * @param unit 228 * the time unit of the timeout argument 229 * 230 * @throws TimeoutException 231 * if the given time elapsed without the command completing 232 */ 233 public void awaitUninterruptibly(long timeout, TimeUnit unit) throws TimeoutException { 234 synchronized (monitor) { 235 final long end = System.currentTimeMillis() + unit.toMillis(timeout); 236 boolean interrupted = false; 237 238 while (state == FutureState.WAITING && System.currentTimeMillis() < end) { 239 try { 240 monitor.wait(end - System.currentTimeMillis()); 241 } catch (InterruptedException e) { 242 interrupted = true; 243 } 244 } 245 246 if (interrupted) { 247 // Restore the interrupt for the caller 248 Thread.currentThread().interrupt(); 249 } 250 251 if (state == FutureState.WAITING) throw new TimeoutException(); 252 } 253 } 254 255 /** 256 * Waits indefinitely until the command completes 257 * and returns the result of the command. 258 * <p> 259 * If the thread is interrupted while waiting for the command 260 * to complete, this method will throw an {@code InterruptedException} 261 * and the thread's interrupt flag will be cleared. 262 * </p><p><i> 263 * Please note that this method is blocking and thus negates 264 * the advantage of the asynchronous nature of this class. 265 * Consider using {@link #onSuccess(SuccessListener)} and 266 * {@link #onFailure(FailureListener)} instead. 267 * </i></p> 268 * 269 * @return the server response to the command 270 * 271 * @throws InterruptedException 272 * if the method is interrupted by calling {@link Thread#interrupt()}. 273 * The interrupt flag will be cleared 274 * @throws CancellationException 275 * if the {@code CommandFuture} was cancelled before the command completed 276 * @throws TS3Exception 277 * if the command fails 278 */ 279 @Override 280 public V get() throws InterruptedException { 281 synchronized (monitor) { 282 await(); 283 284 checkForFailure(); 285 return value; 286 } 287 } 288 289 /** 290 * Waits for at most the given time until the command completes 291 * and returns the result of the command. 292 * <p> 293 * If the thread is interrupted while waiting for the command 294 * to complete, this method will throw an {@code InterruptedException} 295 * and the thread's interrupt flag will be cleared. 296 * </p><p><i> 297 * Please note that this method is blocking and thus negates 298 * the advantage of the asynchronous nature of this class. 299 * Consider using {@link #onSuccess(SuccessListener)} and 300 * {@link #onFailure(FailureListener)} instead. 301 * </i></p> 302 * 303 * @param timeout 304 * the maximum amount of the given time unit to wait 305 * @param unit 306 * the time unit of the timeout argument 307 * 308 * @return the server response to the command 309 * 310 * @throws InterruptedException 311 * if the method is interrupted by calling {@link Thread#interrupt()}. 312 * The interrupt flag will be cleared 313 * @throws TimeoutException 314 * if the given time elapsed without the command completing 315 * @throws CancellationException 316 * if the {@code CommandFuture} was cancelled before the command completed 317 * @throws TS3Exception 318 * if the command fails 319 */ 320 @Override 321 public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { 322 synchronized (monitor) { 323 await(timeout, unit); 324 325 checkForFailure(); 326 return value; 327 } 328 } 329 330 /** 331 * Waits indefinitely until the command completes 332 * and returns the result of the command. 333 * <p> 334 * If the thread is interrupted while waiting for the command 335 * to complete, the interrupt is simply ignored and no 336 * {@link InterruptedException} is thrown. 337 * </p><p><i> 338 * Please note that this method is blocking and thus negates 339 * the advantage of the asynchronous nature of this class. 340 * Consider using {@link #onSuccess(SuccessListener)} and 341 * {@link #onFailure(FailureListener)} instead. 342 * </i></p> 343 * 344 * @return the server response to the command 345 * 346 * @throws CancellationException 347 * if the {@code CommandFuture} was cancelled before the command completed 348 * @throws TS3Exception 349 * if the command fails 350 */ 351 public V getUninterruptibly() { 352 synchronized (monitor) { 353 awaitUninterruptibly(); 354 355 checkForFailure(); 356 return value; 357 } 358 } 359 360 /** 361 * Waits for at most the given time until the command completes 362 * and returns the result of the command. 363 * <p> 364 * If the thread is interrupted while waiting for the command 365 * to complete, the interrupt is simply ignored and no 366 * {@link InterruptedException} is thrown. 367 * </p><p><i> 368 * Please note that this method is blocking and thus negates 369 * the advantage of the asynchronous nature of this class. 370 * Consider using {@link #onSuccess(SuccessListener)} and 371 * {@link #onFailure(FailureListener)} instead. 372 * </i></p> 373 * 374 * @param timeout 375 * the maximum amount of the given time unit to wait 376 * @param unit 377 * the time unit of the timeout argument 378 * 379 * @return the server response to the command 380 * 381 * @throws TimeoutException 382 * if the given time elapsed without the command completing 383 * @throws CancellationException 384 * if the {@code CommandFuture} was cancelled before the command completed 385 * @throws TS3Exception 386 * if the command fails 387 */ 388 public V getUninterruptibly(long timeout, TimeUnit unit) throws TimeoutException { 389 synchronized (monitor) { 390 awaitUninterruptibly(timeout, unit); 391 392 checkForFailure(); 393 return value; 394 } 395 } 396 397 /** 398 * Throws an exception if the future was either cancelled or the command failed. 399 * <p> 400 * <strong>Must be called with the monitor lock held!</strong> 401 * </p> 402 * 403 * @throws CancellationException 404 * if the future was cancelled 405 * @throws TS3Exception 406 * if the command failed 407 */ 408 private void checkForFailure() { 409 if (state == FutureState.CANCELLED) { 410 throw new CancellationException(); 411 } else if (state == FutureState.FAILED) { 412 // Make the stack trace of the exception point to this method and not 413 // SocketReader#run -> TS3ApiAsync#hasFailed, which wouldn't be helpful 414 exception.fillInStackTrace(); 415 throw exception; 416 } 417 } 418 419 @Override 420 public boolean isDone() { 421 return state != FutureState.WAITING; 422 } 423 424 /** 425 * Returns {@code true} if this command completed successfully, 426 * i.e. the future wasn't cancelled and the command completed without throwing an exception. 427 * 428 * @return {@code true} if the command completed successfully 429 */ 430 public boolean isSuccessful() { 431 return state == FutureState.SUCCEEDED; 432 } 433 434 @Override 435 public boolean isCancelled() { 436 return state == FutureState.CANCELLED; 437 } 438 439 /** 440 * Returns {@code true} if the command failed and threw a {@link TS3Exception}. 441 * 442 * @return {@code true} if the command failed 443 */ 444 public boolean hasFailed() { 445 return state == FutureState.FAILED; 446 } 447 448 /** 449 * Sets the value of this future. This will mark the future as successful. 450 * <p> 451 * Furthermore, this will run the {@link SuccessListener}, if one is registered. 452 * All exceptions thrown from the body of the {@code SuccessListener} are caught 453 * so no exceptions can leak into user code. 454 * </p><p> 455 * Note that a future's value can only be set once. Subsequent calls to 456 * this method will be ignored. 457 * </p> 458 * 459 * @param value 460 * the value to set this future to 461 * 462 * @return {@code true} if the command was marked as successful 463 */ 464 public boolean set(V value) { 465 SuccessListener<? super V> listener; 466 467 synchronized (monitor) { 468 if (isDone()) return false; // Ignore 469 470 this.state = FutureState.SUCCEEDED; 471 this.value = value; 472 listener = successListener; 473 monitor.notifyAll(); 474 } 475 476 if (listener != null) { 477 try { 478 listener.handleSuccess(value); 479 } catch (Exception e) { 480 // Whatever happens, we do not want a user error to leak into our logic 481 log.error("SuccessListener threw an exception", e); 482 } 483 } 484 return true; 485 } 486 487 /** 488 * Notifies this future that the command has failed. 489 * <p> 490 * Furthermore, this will run the {@link FailureListener}, if one is registered. 491 * All exceptions thrown from the body of the {@code FailureListener} are caught 492 * so no exceptions can leak into user code. 493 * </p><p> 494 * Note that a future can only fail once. Subsequent calls to this method will be ignored. 495 * </p> 496 * 497 * @param exception 498 * the exception that occurred while executing this command 499 * 500 * @return {@code true} if the command was marked as failed 501 */ 502 public boolean fail(TS3Exception exception) { 503 FailureListener listener; 504 505 synchronized (monitor) { 506 if (isDone()) return false; // Ignore 507 508 this.state = FutureState.FAILED; 509 this.exception = exception; 510 listener = failureListener; 511 monitor.notifyAll(); 512 } 513 514 if (listener != null) { 515 try { 516 listener.handleFailure(exception); 517 } catch (Exception e) { 518 // Whatever happens, we do not want a user error to leak into our logic 519 log.error("FailureListener threw an exception", e); 520 } 521 } 522 return true; 523 } 524 525 /** 526 * {@inheritDoc} 527 * <p> 528 * Cancelling a {@code CommandFuture} will <b>not</b> actually cancel the 529 * execution of the command which was sent to the TeamSpeak server. 530 * </p><p> 531 * It will, however, prevent the {@link SuccessListener} and the 532 * {@link FailureListener} from firing, provided a response from the 533 * server has not yet arrived. 534 * </p> 535 */ 536 @Override 537 public boolean cancel(boolean mayInterruptIfRunning) { 538 synchronized (monitor) { 539 if (isDone()) return false; // Ignore 540 541 this.state = FutureState.CANCELLED; 542 monitor.notifyAll(); 543 } 544 545 return true; 546 } 547 548 /** 549 * Sets a {@link SuccessListener} which will be notified when this future 550 * succeeded and a value has been set. 551 * <p> 552 * If this future has already succeeded, this method will immediately call 553 * the listener method, which will be executed synchronously. 554 * </p> 555 * 556 * @param listener 557 * the listener to notify of a success 558 * 559 * @return this object for chaining 560 */ 561 public CommandFuture<V> onSuccess(SuccessListener<? super V> listener) { 562 boolean runSuccessListener; 563 V successValue; 564 565 synchronized (monitor) { 566 if (successListener != null) { 567 throw new IllegalStateException("Listener already set"); 568 } 569 successListener = listener; 570 571 runSuccessListener = isSuccessful(); 572 successValue = value; 573 } 574 575 if (runSuccessListener) { 576 listener.handleSuccess(successValue); 577 } 578 579 return this; 580 } 581 582 /** 583 * Sets a {@link FailureListener} which will be notified when this future 584 * fails because of a error returned by the TeamSpeak server. 585 * <p> 586 * If this future has already failed, this method will immediately call 587 * the listener method, which will be executed synchronously. 588 * </p> 589 * 590 * @param listener 591 * the listener to notify of a failure 592 * 593 * @return this object for chaining 594 */ 595 public CommandFuture<V> onFailure(FailureListener listener) { 596 boolean runFailureListener; 597 TS3Exception failureException; 598 599 synchronized (monitor) { 600 if (failureListener != null) { 601 throw new IllegalStateException("Listener already set"); 602 } 603 failureListener = listener; 604 605 runFailureListener = hasFailed(); 606 failureException = exception; 607 } 608 609 if (runFailureListener) { 610 listener.handleFailure(failureException); 611 } 612 613 return this; 614 } 615 616 /** 617 * Forwards a success to another future by calling {@link #set(Object)} on 618 * that future with the value this future was set to. 619 * <p> 620 * This will register a {@link SuccessListener}, meaning that you will not 621 * be able to register another {@code SuccessListener}. 622 * </p> 623 * 624 * @param otherFuture 625 * the future to forward a success to 626 * 627 * @return this object for chaining 628 */ 629 public CommandFuture<V> forwardSuccess(final CommandFuture<? super V> otherFuture) { 630 return onSuccess(new SuccessListener<V>() { 631 @Override 632 public void handleSuccess(V result) { 633 otherFuture.set(result); 634 } 635 }); 636 } 637 638 /** 639 * Forwards a failure to another future by calling {@link #fail(TS3Exception)} 640 * on that future with the error that caused this future to fail. 641 * <p> 642 * This will register a {@link FailureListener}, meaning that you will not 643 * be able to register another {@code FailureListener}. 644 * </p> 645 * 646 * @param otherFuture 647 * the future to forward a failure to 648 * 649 * @return this object for chaining 650 */ 651 public CommandFuture<V> forwardFailure(final CommandFuture<?> otherFuture) { 652 return onFailure(new FailureListener() { 653 @Override 654 public void handleFailure(TS3Exception exception) { 655 otherFuture.fail(exception); 656 } 657 }); 658 } 659 660 /** 661 * Forwards both a success as well as a failure to another {@code CommandFuture}. 662 * This method just calls both {@link #forwardSuccess(CommandFuture)} and 663 * {@link #forwardFailure(CommandFuture)}. 664 * <p> 665 * This will set both a {@link SuccessListener} as well as a {@link FailureListener}, 666 * so no other listeners can be registered. 667 * </p> 668 * 669 * @param otherFuture 670 * the future which should be notified about 671 */ 672 public void forwardResult(final CommandFuture<V> otherFuture) { 673 forwardSuccess(otherFuture).forwardFailure(otherFuture); 674 } 675 676 /** 677 * Returns a new {@code CommandFuture} that already has a value set. 678 * 679 * @param value 680 * the default value for the new {@code CommandFuture} 681 * @param <V> 682 * the dynamic type of the value, will usually be inferred 683 * 684 * @return a new {@code CommandFuture} with a default value 685 */ 686 public static <V> CommandFuture<V> immediate(V value) { 687 final CommandFuture<V> future = new CommandFuture<>(); 688 future.set(value); 689 return future; 690 } 691 692 /** 693 * Combines multiple {@code CommandFuture}s into a single future, which will 694 * succeed if all futures succeed and fail as soon as one future fails. 695 * 696 * @param futures 697 * the futures to combine 698 * @param <F> 699 * the common return type of the futures 700 * 701 * @return a future which succeeds if all supplied futures succeed 702 */ 703 @SafeVarargs 704 public static <F> CommandFuture<List<F>> ofAll(CommandFuture<F>... futures) { 705 return ofAll(Arrays.asList(futures)); 706 } 707 708 /** 709 * Combines a collection of {@code CommandFuture}s into a single future, which will 710 * succeed if all futures succeed and fail as soon as one future fails. 711 * 712 * @param futures 713 * the futures to combine 714 * @param <F> 715 * the common return type of the futures 716 * 717 * @return a future which succeeds if all supplied futures succeed 718 */ 719 public static <F> CommandFuture<List<F>> ofAll(final Collection<CommandFuture<F>> futures) { 720 if (futures.isEmpty()) throw new IllegalArgumentException("Requires at least 1 future"); 721 722 @SuppressWarnings("unchecked") final F[] results = (F[]) new Object[futures.size()]; 723 final AtomicInteger successCounter = new AtomicInteger(futures.size()); 724 final CommandFuture<List<F>> combined = new CommandFuture<>(); 725 726 final Iterator<CommandFuture<F>> iterator = futures.iterator(); 727 for (int i = 0; iterator.hasNext(); ++i) { 728 final int index = i; 729 final CommandFuture<F> future = iterator.next(); 730 731 future.forwardFailure(combined).onSuccess(new SuccessListener<F>() { 732 @Override 733 public void handleSuccess(F result) { 734 results[index] = result; 735 736 if (successCounter.decrementAndGet() == 0) { 737 combined.set(Arrays.asList(results)); 738 } 739 } 740 }); 741 } 742 743 return combined; 744 } 745 746 /** 747 * Combines multiple {@code CommandFuture}s into a single future, which will 748 * succeed if any of the futures succeeds and fail if all of the futures fail. 749 * 750 * @param futures 751 * the futures to combine 752 * @param <F> 753 * the common return type of the futures 754 * 755 * @return a future which succeeds if one of the supplied futures succeeds 756 */ 757 @SafeVarargs 758 public static <F> CommandFuture<F> ofAny(CommandFuture<F>... futures) { 759 return ofAny(Arrays.asList(futures)); 760 } 761 762 /** 763 * Combines a collection of {@code CommandFuture}s into a single future, which will 764 * succeed as soon as one of the futures succeeds and fail if all futures fail. 765 * 766 * @param futures 767 * the futures to combine 768 * @param <F> 769 * the common return type of the futures 770 * 771 * @return a future which succeeds if one of the supplied futures succeeds 772 */ 773 public static <F> CommandFuture<F> ofAny(final Collection<CommandFuture<F>> futures) { 774 if (futures.isEmpty()) throw new IllegalArgumentException("Requires at least 1 future"); 775 776 final CommandFuture<F> any = new CommandFuture<>(); 777 final AtomicInteger failureCounter = new AtomicInteger(futures.size()); 778 779 for (CommandFuture<F> future : futures) { 780 future.forwardSuccess(any).onFailure(new FailureListener() { 781 @Override 782 public void handleFailure(TS3Exception exception) { 783 if (failureCounter.decrementAndGet() == 0) { 784 any.fail(exception); 785 } 786 } 787 }); 788 } 789 790 return any; 791 } 792 793 /** 794 * A listener which will be notified if the {@link CommandFuture} succeeded. 795 * In that case, {@link #handleSuccess(Object)} will be called with the value 796 * the future has been set to. 797 * <p> 798 * A {@code CommandFuture}'s {@code SuccessListener} can be set by calling 799 * {@link #onSuccess(SuccessListener)}. 800 * </p> 801 * 802 * @param <V> 803 * the type of the value 804 */ 805 public interface SuccessListener<V> { 806 807 /** 808 * The method to be executed when the command succeeds. 809 * 810 * @param result 811 * the result of the command 812 */ 813 void handleSuccess(V result); 814 } 815 816 /** 817 * A listener which will be notified if the {@link CommandFuture} failed. 818 * In that case, {@link #handleFailure(TS3Exception)} will be called with 819 * the exception that occurred while executing this command. 820 * <p> 821 * A {@code CommandFuture}'s {@code FailureListener} can be set by calling 822 * {@link #onFailure(FailureListener)}. 823 * </p> 824 */ 825 public interface FailureListener { 826 827 /** 828 * The method to be executed when the command failed. 829 * 830 * @param exception 831 * the exception that occurred while executing this command 832 */ 833 void handleFailure(TS3Exception exception); 834 } 835}