/* * Copyright (c) OSGi Alliance (2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.osgi.util.promise; import static org.osgi.util.promise.PromiseImpl.requireNonNull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Static helper methods for {@link Promise}s. * * @ThreadSafe * @author $Id: 06d5066753909c09410f6544115df01c970265b2 $ */ public class Promises { private Promises() { // disallow object creation } /** * Create a new Promise that has been resolved with the specified value. * * @param <T> The value type associated with the returned Promise. * @param value The value of the resolved Promise. * @return A new Promise that has been resolved with the specified value. */ public static <T> Promise<T> resolved(T value) { return new PromiseImpl<T>(value, null); } /** * Create a new Promise that has been resolved with the specified failure. * * @param <T> The value type associated with the returned Promise. * @param failure The failure of the resolved Promise. Must not be * {@code null}. * @return A new Promise that has been resolved with the specified failure. */ public static <T> Promise<T> failed(Throwable failure) { return new PromiseImpl<T>(null, requireNonNull(failure)); } /** * Create a new Promise that is a latch on the resolution of the specified * Promises. * * <p> * The new Promise acts as a gate and must be resolved after all of the * specified Promises are resolved. * * @param <T> The value type of the List value associated with the returned * Promise. * @param <S> A subtype of the value type of the List value associated with * the returned Promise. * @param promises The Promises which must be resolved before the returned * Promise must be resolved. Must not be {@code null} and all of the * elements in the collection must not be {@code null}. * @return A Promise that is resolved only when all the specified Promises * are resolved. The returned Promise must be successfully resolved * with a List of the values in the order of the specified Promises * if all the specified Promises are successfully resolved. The List * in the returned Promise is the property of the caller and is * modifiable. The returned Promise must be resolved with a failure * of {@link FailedPromisesException} if any of the specified * Promises are resolved with a failure. The failure * {@link FailedPromisesException} must contain all of the specified * Promises which resolved with a failure. */ public static <T, S extends T> Promise<List<T>> all(Collection<Promise<S>> promises) { if (promises.isEmpty()) { List<T> result = new ArrayList<T>(); return resolved(result); } /* make a copy and capture the ordering */ List<Promise<? extends T>> list = new ArrayList<Promise<? extends T>>(promises); PromiseImpl<List<T>> chained = new PromiseImpl<List<T>>(); All<T> all = new All<T>(chained, list); for (Promise<? extends T> promise : list) { promise.onResolve(all); } return chained; } /** * Create a new Promise that is a latch on the resolution of the specified * Promises. * * <p> * The new Promise acts as a gate and must be resolved after all of the * specified Promises are resolved. * * @param <T> The value type associated with the specified Promises. * @param promises The Promises which must be resolved before the returned * Promise must be resolved. Must not be {@code null} and all of the * arguments must not be {@code null}. * @return A Promise that is resolved only when all the specified Promises * are resolved. The returned Promise must be successfully resolved * with a List of the values in the order of the specified Promises * if all the specified Promises are successfully resolved. The List * in the returned Promise is the property of the caller and is * modifiable. The returned Promise must be resolved with a failure * of {@link FailedPromisesException} if any of the specified * Promises are resolved with a failure. The failure * {@link FailedPromisesException} must contain all of the specified * Promises which resolved with a failure. */ public static <T> Promise<List<T>> all(Promise<? extends T>... promises) { @SuppressWarnings("unchecked") List<Promise<T>> list = Arrays.asList((Promise<T>[]) promises); return all(list); } /** * A callback used to resolve a Promise when the specified list of Promises * are resolved for the {@link Promises#all(Collection)} method. * * @ThreadSafe */ private static final class All<T> implements Runnable { private final PromiseImpl<List<T>> chained; private final List<Promise<? extends T>> promises; private final AtomicInteger promiseCount; All(PromiseImpl<List<T>> chained, List<Promise<? extends T>> promises) { this.chained = chained; this.promises = promises; this.promiseCount = new AtomicInteger(promises.size()); } public void run() { if (promiseCount.decrementAndGet() != 0) { return; } List<T> result = new ArrayList<T>(promises.size()); List<Promise<?>> failed = new ArrayList<Promise<?>>(promises.size()); Throwable cause = null; for (Promise<? extends T> promise : promises) { Throwable failure; T value; try { failure = promise.getFailure(); value = (failure != null) ? null : promise.getValue(); } catch (Throwable e) { chained.resolve(null, e); return; } if (failure != null) { failed.add(promise); if (cause == null) { cause = failure; } } else { result.add(value); } } if (failed.isEmpty()) { chained.resolve(result, null); } else { chained.resolve(null, new FailedPromisesException(failed, cause)); } } } }