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