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