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}