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