Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2016-12-07 16:02:27 +0000
committerThomas Watson2017-01-11 16:08:16 +0000
commit86ac868ae86d1ff21292919d47a6dbf46075d3a8 (patch)
tree54e19a5e50b418afc9c8ecd4d21d775c257ffee6 /bundles/org.eclipse.osgi.util/src
parentdd171a7ac305c0b5df769731037a76bf645d797e (diff)
downloadrt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.tar.gz
rt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.tar.xz
rt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.zip
Bug 501950 - [ds] replace Equinox DS implementation with Felix SCR (DS)I20170112-0245I20170112-0200I20170111-2000
implementation Update the DS API for R6 Change-Id: I49f46a8474a4b434ad20fe54f2ad8f0f4d649e99 Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
Diffstat (limited to 'bundles/org.eclipse.osgi.util/src')
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java43
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java43
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java40
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo1
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java142
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java55
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java61
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java403
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java615
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java180
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java69
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java40
-rw-r--r--bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo1
13 files changed, 1693 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java
new file mode 100644
index 000000000..5d812f75c
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java
@@ -0,0 +1,43 @@
+/*
+ * 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.function;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A function that accepts a single argument and produces a result.
+ *
+ * <p>
+ * This is a functional interface and can be used as the assignment target for a
+ * lambda expression or method reference.
+ *
+ * @param <T> The type of the function input.
+ * @param <R> The type of the function output.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+@ConsumerType
+public interface Function<T, R> {
+ /**
+ * Applies this function to the specified argument.
+ *
+ * @param t The input to this function.
+ * @return The output of this function.
+ */
+ R apply(T t);
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java
new file mode 100644
index 000000000..0c2c61f78
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java
@@ -0,0 +1,43 @@
+/*
+ * 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.function;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A predicate that accepts a single argument and produces a boolean result.
+ *
+ * <p>
+ * This is a functional interface and can be used as the assignment target for a
+ * lambda expression or method reference.
+ *
+ * @param <T> The type of the predicate input.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+@ConsumerType
+public interface Predicate<T> {
+ /**
+ * Evaluates this predicate on the specified argument.
+ *
+ * @param t The input to this predicate.
+ * @return {@code true} if the specified argument is accepted by this
+ * predicate; {@code false} otherwise.
+ */
+ boolean test(T t);
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java
new file mode 100644
index 000000000..899d786b2
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * Function Package Version 1.0.
+ *
+ * <p>
+ * Bundles wishing to use this package must list the package in the
+ * Import-Package header of the bundle's manifest.
+ *
+ * <p>
+ * Example import for consumers using the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.util.function; version="[1.0,2.0)"}
+ * <p>
+ * Example import for providers implementing the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.util.function; version="[1.0,1.1)"}
+ *
+ * @author $Id$
+ */
+
+@Version("1.0")
+package org.osgi.util.function;
+
+import org.osgi.annotation.versioning.Version;
+
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo
new file mode 100644
index 000000000..7c8de0324
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java
new file mode 100644
index 000000000..e9ff0a6fa
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+/**
+ * A Deferred Promise resolution.
+ *
+ * <p>
+ * Instances of this class can be used to create a {@link Promise} that can be
+ * resolved in the future. The {@link #getPromise() associated} Promise can be
+ * successfully resolved with {@link #resolve(Object)} or resolved with a
+ * failure with {@link #fail(Throwable)}. It can also be resolved with the
+ * resolution of another promise using {@link #resolveWith(Promise)}.
+ *
+ * <p>
+ * The associated Promise can be provided to any one, but the Deferred object
+ * should be made available only to the party that will responsible for
+ * resolving the Promise.
+ *
+ * @param <T> The value type associated with the created Promise.
+ *
+ * @Immutable
+ * @author $Id$
+ */
+public class Deferred<T> {
+ private final PromiseImpl<T> promise;
+
+ /**
+ * Create a new Deferred with an associated Promise.
+ */
+ public Deferred() {
+ promise = new PromiseImpl<T>();
+ }
+
+ /**
+ * Returns the Promise associated with this Deferred.
+ *
+ * @return The Promise associated with this Deferred.
+ */
+ public Promise<T> getPromise() {
+ return promise;
+ }
+
+ /**
+ * Successfully resolve the Promise associated with this Deferred.
+ *
+ * <p>
+ * After the associated Promise is resolved with the specified value, all
+ * registered {@link Promise#onResolve(Runnable) callbacks} are called and
+ * any {@link Promise#then(Success, Failure) chained} Promises are resolved.
+ *
+ * <p>
+ * Resolving the associated Promise <i>happens-before</i> any registered
+ * callback is called. That is, in a registered callback,
+ * {@link Promise#isDone()} must return {@code true} and
+ * {@link Promise#getValue()} and {@link Promise#getFailure()} must not
+ * block.
+ *
+ * @param value The value of the resolved Promise.
+ * @throws IllegalStateException If the associated Promise was already
+ * resolved.
+ */
+ public void resolve(T value) {
+ promise.resolve(value, null);
+ }
+
+ /**
+ * Fail the Promise associated with this Deferred.
+ *
+ * <p>
+ * After the associated Promise is resolved with the specified failure, all
+ * registered {@link Promise#onResolve(Runnable) callbacks} are called and
+ * any {@link Promise#then(Success, Failure) chained} Promises are resolved.
+ *
+ * <p>
+ * Resolving the associated Promise <i>happens-before</i> any registered
+ * callback is called. That is, in a registered callback,
+ * {@link Promise#isDone()} must return {@code true} and
+ * {@link Promise#getValue()} and {@link Promise#getFailure()} must not
+ * block.
+ *
+ * @param failure The failure of the resolved Promise. Must not be
+ * {@code null}.
+ * @throws IllegalStateException If the associated Promise was already
+ * resolved.
+ */
+ public void fail(Throwable failure) {
+ promise.resolve(null, requireNonNull(failure));
+ }
+
+ /**
+ * Resolve the Promise associated with this Deferred with the specified
+ * Promise.
+ *
+ * <p>
+ * If the specified Promise is successfully resolved, the associated Promise
+ * is resolved with the value of the specified Promise. If the specified
+ * Promise is resolved with a failure, the associated Promise is resolved
+ * with the failure of the specified Promise.
+ *
+ * <p>
+ * After the associated Promise is resolved with the specified Promise, all
+ * registered {@link Promise#onResolve(Runnable) callbacks} are called and
+ * any {@link Promise#then(Success, Failure) chained} Promises are resolved.
+ *
+ * <p>
+ * Resolving the associated Promise <i>happens-before</i> any registered
+ * callback is called. That is, in a registered callback,
+ * {@link Promise#isDone()} must return {@code true} and
+ * {@link Promise#getValue()} and {@link Promise#getFailure()} must not
+ * block.
+ *
+ * @param with A Promise whose value or failure must be used to resolve the
+ * associated Promise. Must not be {@code null}.
+ * @return A Promise that is resolved only when the associated Promise is
+ * resolved by the specified Promise. The returned Promise must be
+ * successfully resolved with the value {@code null}, if the
+ * associated Promise was resolved by the specified Promise. The
+ * returned Promise must be resolved with a failure of
+ * {@link IllegalStateException}, if the associated Promise was
+ * already resolved when the specified Promise was resolved.
+ */
+ public Promise<Void> resolveWith(Promise<? extends T> with) {
+ return promise.resolveWith(with);
+ }
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java
new file mode 100644
index 000000000..95546abe5
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java
@@ -0,0 +1,55 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Promise failure exception for a collection of failed Promises.
+ *
+ * @author $Id$
+ */
+public class FailedPromisesException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ private final Collection<Promise<?>> failed;
+
+ /**
+ * Create a new FailedPromisesException with the specified Promises.
+ *
+ * @param failed A collection of Promises that have been resolved with a
+ * failure. Must not be {@code null}, must not be empty and all of
+ * the elements in the collection must not be {@code null}.
+ * @param cause The cause of this exception. This is typically the failure
+ * of the first Promise in the specified collection.
+ */
+ public FailedPromisesException(Collection<Promise<?>> failed, Throwable cause) {
+ super(cause);
+ this.failed = Collections.unmodifiableCollection(failed);
+ }
+
+ /**
+ * Returns the collection of Promises that have been resolved with a
+ * failure.
+ *
+ * @return The collection of Promises that have been resolved with a
+ * failure. The returned collection is unmodifiable.
+ */
+ public Collection<Promise<?>> getFailedPromises() {
+ return failed;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java
new file mode 100644
index 000000000..4e6a17884
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Failure callback for a Promise.
+ *
+ * <p>
+ * A Failure callback is registered with a {@link Promise} using the
+ * {@link Promise#then(Success, Failure)} method and is called if the Promise is
+ * resolved with a failure.
+ *
+ * <p>
+ * This is a functional interface and can be used as the assignment target for a
+ * lambda expression or method reference.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+@ConsumerType
+public interface Failure {
+ /**
+ * Failure callback for a Promise.
+ *
+ * <p>
+ * This method is called if the Promise with which it is registered resolves
+ * with a failure.
+ *
+ * <p>
+ * In the remainder of this description we will refer to the Promise
+ * returned by {@link Promise#then(Success, Failure)} when this Failure
+ * callback was registered as the chained Promise.
+ *
+ * <p>
+ * If this methods completes normally, the chained Promise must be failed
+ * with the same exception which failed the resolved Promise. If this method
+ * throws an exception, the chained Promise must be failed with the thrown
+ * exception.
+ *
+ * @param resolved The failed resolved {@link Promise}.
+ * @throws Exception The chained Promise must be failed with the thrown
+ * exception.
+ */
+ void fail(Promise<?> resolved) throws Exception;
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java
new file mode 100644
index 000000000..ffa193393
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java
@@ -0,0 +1,403 @@
+/*
+ * 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 java.lang.reflect.InvocationTargetException;
+import org.osgi.annotation.versioning.ProviderType;
+import org.osgi.util.function.Function;
+import org.osgi.util.function.Predicate;
+
+/**
+ * A Promise of a value.
+ *
+ * <p>
+ * A Promise represents a future value. It handles the interactions for
+ * asynchronous processing. A {@link Deferred} object can be used to create a
+ * Promise and later resolve the Promise. A Promise is used by the caller of an
+ * asynchronous function to get the result or handle the error. The caller can
+ * either get a callback when the Promise is resolved with a value or an error,
+ * or the Promise can be used in chaining. In chaining, callbacks are provided
+ * that receive the resolved Promise, and a new Promise is generated that
+ * resolves based upon the result of a callback.
+ *
+ * <p>
+ * Both {@link #onResolve(Runnable) callbacks} and
+ * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+ * even after the Promise has been resolved.
+ * <p>
+ * Example callback usage:
+ *
+ * <pre>
+ * final Promise&lt;String&gt; foo = foo();
+ * foo.onResolve(new Runnable() {
+ * public void run() {
+ * System.out.println(foo.getValue());
+ * }
+ * });
+ * </pre>
+ *
+ * Example chaining usage;
+ *
+ * <pre>
+ * Success&lt;String,String&gt; doubler = new Success&lt;String,String&gt;() {
+ * public Promise&lt;String&gt; call(Promise&lt;String&gt; p) throws Exception {
+ * return Promises.resolved(p.getValue()+p.getValue());
+ * }
+ * };
+ * final Promise&lt;String&gt; foo = foo().then(doubler).then(doubler);
+ * foo.onResolve(new Runnable() {
+ * public void run() {
+ * System.out.println(foo.getValue());
+ * }
+ * });
+ * </pre>
+ *
+ * @param <T> The value type associated with this Promise.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+@ProviderType
+public interface Promise<T> {
+
+ /**
+ * Returns whether this Promise has been resolved.
+ *
+ * <p>
+ * This Promise may be successfully resolved or resolved with a failure.
+ *
+ * @return {@code true} if this Promise was resolved either successfully or
+ * with a failure; {@code false} if this Promise is unresolved.
+ */
+ boolean isDone();
+
+ /**
+ * Returns the value of this Promise.
+ *
+ * <p>
+ * If this Promise is not {@link #isDone() resolved}, this method must block
+ * and wait for this Promise to be resolved before completing.
+ *
+ * <p>
+ * If this Promise was successfully resolved, this method returns with the
+ * value of this Promise. If this Promise was resolved with a failure, this
+ * method must throw an {@code InvocationTargetException} with the
+ * {@link #getFailure() failure exception} as the cause.
+ *
+ * @return The value of this resolved Promise.
+ * @throws InvocationTargetException If this Promise was resolved with a
+ * failure. The cause of the {@code InvocationTargetException} is
+ * the failure exception.
+ * @throws InterruptedException If the current thread was interrupted while
+ * waiting.
+ */
+ T getValue() throws InvocationTargetException, InterruptedException;
+
+ /**
+ * Returns the failure of this Promise.
+ *
+ * <p>
+ * If this Promise is not {@link #isDone() resolved}, this method must block
+ * and wait for this Promise to be resolved before completing.
+ *
+ * <p>
+ * If this Promise was resolved with a failure, this method returns with the
+ * failure of this Promise. If this Promise was successfully resolved, this
+ * method must return {@code null}.
+ *
+ * @return The failure of this resolved Promise or {@code null} if this
+ * Promise was successfully resolved.
+ * @throws InterruptedException If the current thread was interrupted while
+ * waiting.
+ */
+ Throwable getFailure() throws InterruptedException;
+
+ /**
+ * Register a callback to be called when this Promise is resolved.
+ *
+ * <p>
+ * The specified callback is called when this Promise is resolved either
+ * successfully or with a failure.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * <p>
+ * Resolving this Promise <i>happens-before</i> any registered callback is
+ * called. That is, in a registered callback, {@link #isDone()} must return
+ * {@code true} and {@link #getValue()} and {@link #getFailure()} must not
+ * block.
+ *
+ * <p>
+ * A callback may be called on a different thread than the thread which
+ * registered the callback. So the callback must be thread safe but can rely
+ * upon that the registration of the callback <i>happens-before</i> the
+ * registered callback is called.
+ *
+ * @param callback A callback to be called when this Promise is resolved.
+ * Must not be {@code null}.
+ * @return This Promise.
+ */
+ Promise<T> onResolve(Runnable callback);
+
+ /**
+ * Chain a new Promise to this Promise with Success and Failure callbacks.
+ *
+ * <p>
+ * The specified {@link Success} callback is called when this Promise is
+ * successfully resolved and the specified {@link Failure} callback is
+ * called when this Promise is resolved with a failure.
+ *
+ * <p>
+ * This method returns a new Promise which is chained to this Promise. The
+ * returned Promise must be resolved when this Promise is resolved after the
+ * specified Success or Failure callback is executed. The result of the
+ * executed callback must be used to resolve the returned Promise. Multiple
+ * calls to this method can be used to create a chain of promises which are
+ * resolved in sequence.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the Success callback is
+ * executed and the result Promise, if any, or thrown exception is used to
+ * resolve the returned Promise from this method. If this Promise is
+ * resolved with a failure, the Failure callback is executed and the
+ * returned Promise from this method is failed.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * <p>
+ * Resolving this Promise <i>happens-before</i> any registered callback is
+ * called. That is, in a registered callback, {@link #isDone()} must return
+ * {@code true} and {@link #getValue()} and {@link #getFailure()} must not
+ * block.
+ *
+ * <p>
+ * A callback may be called on a different thread than the thread which
+ * registered the callback. So the callback must be thread safe but can rely
+ * upon that the registration of the callback <i>happens-before</i> the
+ * registered callback is called.
+ *
+ * @param <R> The value type associated with the returned Promise.
+ * @param success A Success callback to be called when this Promise is
+ * successfully resolved. May be {@code null} if no Success callback
+ * is required. In this case, the returned Promise must be resolved
+ * with the value {@code null} when this Promise is successfully
+ * resolved.
+ * @param failure A Failure callback to be called when this Promise is
+ * resolved with a failure. May be {@code null} if no Failure
+ * callback is required.
+ * @return A new Promise which is chained to this Promise. The returned
+ * Promise must be resolved when this Promise is resolved after the
+ * specified Success or Failure callback, if any, is executed.
+ */
+ <R> Promise<R> then(Success<? super T, ? extends R> success, Failure failure);
+
+ /**
+ * Chain a new Promise to this Promise with a Success callback.
+ *
+ * <p>
+ * This method performs the same function as calling
+ * {@link #then(Success, Failure)} with the specified Success callback and
+ * {@code null} for the Failure callback.
+ *
+ * @param <R> The value type associated with the returned Promise.
+ * @param success A Success callback to be called when this Promise is
+ * successfully resolved. May be {@code null} if no Success callback
+ * is required. In this case, the returned Promise must be resolved
+ * with the value {@code null} when this Promise is successfully
+ * resolved.
+ * @return A new Promise which is chained to this Promise. The returned
+ * Promise must be resolved when this Promise is resolved after the
+ * specified Success, if any, is executed.
+ * @see #then(Success, Failure)
+ */
+ <R> Promise<R> then(Success<? super T, ? extends R> success);
+
+ /**
+ * Filter the value of this Promise.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must
+ * either be resolved with the value of this Promise, if the specified
+ * Predicate accepts that value, or failed with a
+ * {@code NoSuchElementException}, if the specified Predicate does not
+ * accept that value. If the specified Predicate throws an exception, the
+ * returned Promise must be failed with the exception.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the returned Promise must be
+ * failed with that failure.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param predicate The Predicate to evaluate the value of this Promise.
+ * Must not be {@code null}.
+ * @return A Promise that filters the value of this Promise.
+ */
+ Promise<T> filter(Predicate<? super T> predicate);
+
+ /**
+ * Map the value of this Promise.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must be
+ * resolved with the value of specified Function as applied to the value of
+ * this Promise. If the specified Function throws an exception, the returned
+ * Promise must be failed with the exception.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the returned Promise must be
+ * failed with that failure.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param <R> The value type associated with the returned Promise.
+ * @param mapper The Function that must map the value of this Promise to the
+ * value that must be used to resolve the returned Promise. Must not
+ * be {@code null}.
+ * @return A Promise that returns the value of this Promise as mapped by the
+ * specified Function.
+ */
+ <R> Promise<R> map(Function<? super T, ? extends R> mapper);
+
+ /**
+ * FlatMap the value of this Promise.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must be
+ * resolved with the Promise from the specified Function as applied to the
+ * value of this Promise. If the specified Function throws an exception, the
+ * returned Promise must be failed with the exception.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the returned Promise must be
+ * failed with that failure.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param <R> The value type associated with the returned Promise.
+ * @param mapper The Function that must flatMap the value of this Promise to
+ * a Promise that must be used to resolve the returned Promise. Must
+ * not be {@code null}.
+ * @return A Promise that returns the value of this Promise as mapped by the
+ * specified Function.
+ */
+ <R> Promise<R> flatMap(Function<? super T, Promise<? extends R>> mapper);
+
+ /**
+ * Recover from a failure of this Promise with a recovery value.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must be
+ * resolved with the value of this Promise.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the specified Function is
+ * applied to this Promise to produce a recovery value.
+ * <ul>
+ * <li>If the recovery value is not {@code null}, the returned Promise must
+ * be resolved with the recovery value.</li>
+ * <li>If the recovery value is {@code null}, the returned Promise must be
+ * failed with the failure of this Promise.</li>
+ * <li>If the specified Function throws an exception, the returned Promise
+ * must be failed with that exception.</li>
+ * </ul>
+ *
+ * <p>
+ * To recover from a failure of this Promise with a recovery value of
+ * {@code null}, the {@link #recoverWith(Function)} method must be used. The
+ * specified Function for {@link #recoverWith(Function)} can return
+ * {@code Promises.resolved(null)} to supply the desired {@code null} value.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param recovery If this Promise resolves with a failure, the specified
+ * Function is called to produce a recovery value to be used to
+ * resolve the returned Promise. Must not be {@code null}.
+ * @return A Promise that resolves with the value of this Promise or
+ * recovers from the failure of this Promise.
+ */
+ Promise<T> recover(Function<Promise<?>, ? extends T> recovery);
+
+ /**
+ * Recover from a failure of this Promise with a recovery Promise.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must be
+ * resolved with the value of this Promise.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the specified Function is
+ * applied to this Promise to produce a recovery Promise.
+ * <ul>
+ * <li>If the recovery Promise is not {@code null}, the returned Promise
+ * must be resolved with the recovery Promise.</li>
+ * <li>If the recovery Promise is {@code null}, the returned Promise must be
+ * failed with the failure of this Promise.</li>
+ * <li>If the specified Function throws an exception, the returned Promise
+ * must be failed with that exception.</li>
+ * </ul>
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param recovery If this Promise resolves with a failure, the specified
+ * Function is called to produce a recovery Promise to be used to
+ * resolve the returned Promise. Must not be {@code null}.
+ * @return A Promise that resolves with the value of this Promise or
+ * recovers from the failure of this Promise.
+ */
+ Promise<T> recoverWith(Function<Promise<?>, Promise<? extends T>> recovery);
+
+ /**
+ * Fall back to the value of the specified Promise if this Promise fails.
+ *
+ * <p>
+ * If this Promise is successfully resolved, the returned Promise must be
+ * resolved with the value of this Promise.
+ *
+ * <p>
+ * If this Promise is resolved with a failure, the successful result of the
+ * specified Promise is used to resolve the returned Promise. If the
+ * specified Promise is resolved with a failure, the returned Promise must
+ * be failed with the failure of this Promise rather than the failure of the
+ * specified Promise.
+ *
+ * <p>
+ * This method may be called at any time including before and after this
+ * Promise has been resolved.
+ *
+ * @param fallback The Promise whose value must be used to resolve the
+ * returned Promise if this Promise resolves with a failure. Must not
+ * be {@code null}.
+ * @return A Promise that returns the value of this Promise or falls back to
+ * the value of the specified Promise.
+ */
+ Promise<T> fallbackTo(Promise<? extends T> fallback);
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java
new file mode 100644
index 000000000..ca2be5e80
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java
@@ -0,0 +1,615 @@
+/*
+ * 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 java.lang.reflect.InvocationTargetException;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import org.osgi.util.function.Function;
+import org.osgi.util.function.Predicate;
+
+/**
+ * Promise implementation.
+ *
+ * <p>
+ * This class is not used directly by clients. Clients should use
+ * {@link Deferred} to create a resolvable {@link Promise}.
+ *
+ * @param <T> The result type associated with the Promise.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+final class PromiseImpl<T> implements Promise<T> {
+ /**
+ * A ConcurrentLinkedQueue to hold the callbacks for this Promise, so no
+ * additional synchronization is required to write to or read from the
+ * queue.
+ */
+ private final ConcurrentLinkedQueue<Runnable> callbacks;
+ /**
+ * A CountDownLatch to manage the resolved state of this Promise.
+ *
+ * <p>
+ * This object is used as the synchronizing object to provide a critical
+ * section in {@link #resolve(Object, Throwable)} so that only a single
+ * thread can write the resolved state variables and open the latch.
+ *
+ * <p>
+ * The resolved state variables, {@link #value} and {@link #fail}, must only
+ * be written when the latch is closed (getCount() != 0) and must only be
+ * read when the latch is open (getCount() == 0). The latch state must
+ * always be checked before writing or reading since the resolved state
+ * variables' memory consistency is guarded by the latch.
+ */
+ private final CountDownLatch resolved;
+ /**
+ * The value of this Promise if successfully resolved.
+ *
+ * @GuardedBy("resolved")
+ * @see #resolved
+ */
+ private T value;
+ /**
+ * The failure of this Promise if resolved with a failure or {@code null} if
+ * successfully resolved.
+ *
+ * @GuardedBy("resolved")
+ * @see #resolved
+ */
+ private Throwable fail;
+
+ /**
+ * Initialize this Promise.
+ */
+ PromiseImpl() {
+ callbacks = new ConcurrentLinkedQueue<Runnable>();
+ resolved = new CountDownLatch(1);
+ }
+
+ /**
+ * Initialize and resolve this Promise.
+ *
+ * @param v The value of this resolved Promise.
+ * @param f The failure of this resolved Promise.
+ */
+ PromiseImpl(T v, Throwable f) {
+ value = v;
+ fail = f;
+ callbacks = new ConcurrentLinkedQueue<Runnable>();
+ resolved = new CountDownLatch(0);
+ }
+
+ /**
+ * Resolve this Promise.
+ *
+ * @param v The value of this Promise.
+ * @param f The failure of this Promise.
+ */
+ void resolve(T v, Throwable f) {
+ // critical section: only one resolver at a time
+ synchronized (resolved) {
+ if (resolved.getCount() == 0) {
+ throw new IllegalStateException("Already resolved");
+ }
+ /*
+ * The resolved state variables must be set before opening the
+ * latch. This safely publishes them to be read by other threads
+ * that must verify the latch is open before reading.
+ */
+ value = v;
+ fail = f;
+ resolved.countDown();
+ }
+ notifyCallbacks(); // call any registered callbacks
+ }
+
+ /**
+ * Call any registered callbacks if this Promise is resolved.
+ */
+ private void notifyCallbacks() {
+ if (resolved.getCount() != 0) {
+ return; // return if not resolved
+ }
+
+ /*
+ * Note: multiple threads can be in this method removing callbacks from
+ * the queue and calling them, so the order in which callbacks are
+ * called cannot be specified.
+ */
+ for (Runnable callback = callbacks.poll(); callback != null; callback = callbacks.poll()) {
+ try {
+ callback.run();
+ } catch (Throwable t) {
+ Logger.logCallbackException(t);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDone() {
+ return resolved.getCount() == 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getValue() throws InvocationTargetException, InterruptedException {
+ resolved.await();
+ if (fail == null) {
+ return value;
+ }
+ throw new InvocationTargetException(fail);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Throwable getFailure() throws InterruptedException {
+ resolved.await();
+ return fail;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Promise<T> onResolve(Runnable callback) {
+ callbacks.offer(callback);
+ notifyCallbacks(); // call any registered callbacks
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <R> Promise<R> then(Success<? super T, ? extends R> success, Failure failure) {
+ PromiseImpl<R> chained = new PromiseImpl<R>();
+ onResolve(new Then<R>(chained, success, failure));
+ return chained;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <R> Promise<R> then(Success<? super T, ? extends R> success) {
+ return then(success, null);
+ }
+
+ /**
+ * A callback used to chain promises for the {@link #then(Success, Failure)}
+ * method.
+ *
+ * @Immutable
+ */
+ private final class Then<R> implements Runnable {
+ private final PromiseImpl<R> chained;
+ private final Success<T, ? extends R> success;
+ private final Failure failure;
+
+ @SuppressWarnings("unchecked")
+ Then(PromiseImpl<R> chained, Success<? super T, ? extends R> success, Failure failure) {
+ this.chained = chained;
+ this.success = (Success<T, ? extends R>) success;
+ this.failure = failure;
+ }
+
+ public void run() {
+ Throwable f;
+ final boolean interrupted = Thread.interrupted();
+ try {
+ f = getFailure();
+ } catch (Throwable e) {
+ f = e; // propagate new exception
+ } finally {
+ if (interrupted) { // restore interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (f != null) {
+ if (failure != null) {
+ try {
+ failure.fail(PromiseImpl.this);
+ } catch (Throwable e) {
+ f = e; // propagate new exception
+ }
+ }
+ // fail chained
+ chained.resolve(null, f);
+ return;
+ }
+ Promise<? extends R> returned = null;
+ if (success != null) {
+ try {
+ returned = success.call(PromiseImpl.this);
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return;
+ }
+ }
+ if (returned == null) {
+ // resolve chained with null value
+ chained.resolve(null, null);
+ } else {
+ // resolve chained when returned promise is resolved
+ returned.onResolve(new Chain<R>(chained, returned));
+ }
+ }
+ }
+
+ /**
+ * A callback used to resolve the chained Promise when the Promise promise
+ * is resolved.
+ *
+ * @Immutable
+ */
+ private final static class Chain<R> implements Runnable {
+ private final PromiseImpl<R> chained;
+ private final Promise<? extends R> promise;
+ private final Throwable failure;
+
+ Chain(PromiseImpl<R> chained, Promise<? extends R> promise) {
+ this.chained = chained;
+ this.promise = promise;
+ this.failure = null;
+ }
+
+ Chain(PromiseImpl<R> chained, Promise<? extends R> promise, Throwable failure) {
+ this.chained = chained;
+ this.promise = promise;
+ this.failure = failure;
+ }
+
+ public void run() {
+ R value = null;
+ Throwable f;
+ final boolean interrupted = Thread.interrupted();
+ try {
+ f = promise.getFailure();
+ if (f == null) {
+ value = promise.getValue();
+ } else if (failure != null) {
+ f = failure;
+ }
+ } catch (Throwable e) {
+ f = e; // propagate new exception
+ } finally {
+ if (interrupted) { // restore interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+ chained.resolve(value, f);
+ }
+ }
+
+ /**
+ * Resolve this Promise with the specified Promise.
+ *
+ * <p>
+ * If the specified Promise is successfully resolved, this Promise is
+ * resolved with the value of the specified Promise. If the specified
+ * Promise is resolved with a failure, this Promise is resolved with the
+ * failure of the specified Promise.
+ *
+ * @param with A Promise whose value or failure must be used to resolve this
+ * Promise. Must not be {@code null}.
+ * @return A Promise that is resolved only when this Promise is resolved by
+ * the specified Promise. The returned Promise must be successfully
+ * resolved with the value {@code null}, if this Promise was
+ * resolved by the specified Promise. The returned Promise must be
+ * resolved with a failure of {@link IllegalStateException}, if this
+ * Promise was already resolved when the specified Promise was
+ * resolved.
+ */
+ Promise<Void> resolveWith(Promise<? extends T> with) {
+ PromiseImpl<Void> chained = new PromiseImpl<Void>();
+ ResolveWith resolveWith = new ResolveWith(chained);
+ with.then(resolveWith, resolveWith);
+ return chained;
+ }
+
+ /**
+ * A callback used to resolve this Promise with another Promise for the
+ * {@link PromiseImpl#resolveWith(Promise)} method.
+ *
+ * @Immutable
+ */
+ private final class ResolveWith implements Success<T, Void>, Failure {
+ private final PromiseImpl<Void> chained;
+
+ ResolveWith(PromiseImpl<Void> chained) {
+ this.chained = chained;
+ }
+
+ public Promise<Void> call(Promise<T> with) throws Exception {
+ try {
+ resolve(with.getValue(), null);
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return null;
+ }
+ chained.resolve(null, null);
+ return null;
+ }
+
+ public void fail(Promise<?> with) throws Exception {
+ try {
+ resolve(null, with.getFailure());
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return;
+ }
+ chained.resolve(null, null);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Promise<T> filter(Predicate<? super T> predicate) {
+ return then(new Filter<T>(predicate));
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#filter(Predicate)} method.
+ *
+ * @Immutable
+ */
+ private static final class Filter<T> implements Success<T, T> {
+ private final Predicate<? super T> predicate;
+
+ Filter(Predicate<? super T> predicate) {
+ this.predicate = requireNonNull(predicate);
+ }
+
+ public Promise<T> call(Promise<T> resolved) throws Exception {
+ if (predicate.test(resolved.getValue())) {
+ return resolved;
+ }
+ throw new NoSuchElementException();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <R> Promise<R> map(Function<? super T, ? extends R> mapper) {
+ return then(new Map<T, R>(mapper));
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#map(Function)} method.
+ *
+ * @Immutable
+ */
+ private static final class Map<T, R> implements Success<T, R> {
+ private final Function<? super T, ? extends R> mapper;
+
+ Map(Function<? super T, ? extends R> mapper) {
+ this.mapper = requireNonNull(mapper);
+ }
+
+ public Promise<R> call(Promise<T> resolved) throws Exception {
+ return new PromiseImpl<R>(mapper.apply(resolved.getValue()), null);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <R> Promise<R> flatMap(Function<? super T, Promise<? extends R>> mapper) {
+ return then(new FlatMap<T, R>(mapper));
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#flatMap(Function)} method.
+ *
+ * @Immutable
+ */
+ private static final class FlatMap<T, R> implements Success<T, R> {
+ private final Function<? super T, Promise<? extends R>> mapper;
+
+ FlatMap(Function<? super T, Promise<? extends R>> mapper) {
+ this.mapper = requireNonNull(mapper);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Promise<R> call(Promise<T> resolved) throws Exception {
+ return (Promise<R>) mapper.apply(resolved.getValue());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Promise<T> recover(Function<Promise<?>, ? extends T> recovery) {
+ PromiseImpl<T> chained = new PromiseImpl<T>();
+ Recover<T> recover = new Recover<T>(chained, recovery);
+ then(recover, recover);
+ return chained;
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#recover(Function)} method.
+ *
+ * @Immutable
+ */
+ private static final class Recover<T> implements Success<T, Void>, Failure {
+ private final PromiseImpl<T> chained;
+ private final Function<Promise<?>, ? extends T> recovery;
+
+ Recover(PromiseImpl<T> chained, Function<Promise<?>, ? extends T> recovery) {
+ this.chained = chained;
+ this.recovery = requireNonNull(recovery);
+ }
+
+ public Promise<Void> call(Promise<T> resolved) throws Exception {
+ T value;
+ try {
+ value = resolved.getValue();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return null;
+ }
+ chained.resolve(value, null);
+ return null;
+ }
+
+ public void fail(Promise<?> resolved) throws Exception {
+ T recovered;
+ Throwable failure;
+ try {
+ recovered = recovery.apply(resolved);
+ failure = resolved.getFailure();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return;
+ }
+ if (recovered == null) {
+ chained.resolve(null, failure);
+ } else {
+ chained.resolve(recovered, null);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Promise<T> recoverWith(Function<Promise<?>, Promise<? extends T>> recovery) {
+ PromiseImpl<T> chained = new PromiseImpl<T>();
+ RecoverWith<T> recoverWith = new RecoverWith<T>(chained, recovery);
+ then(recoverWith, recoverWith);
+ return chained;
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#recoverWith(Function)} method.
+ *
+ * @Immutable
+ */
+ private static final class RecoverWith<T> implements Success<T, Void>, Failure {
+ private final PromiseImpl<T> chained;
+ private final Function<Promise<?>, Promise<? extends T>> recovery;
+
+ RecoverWith(PromiseImpl<T> chained, Function<Promise<?>, Promise<? extends T>> recovery) {
+ this.chained = chained;
+ this.recovery = requireNonNull(recovery);
+ }
+
+ public Promise<Void> call(Promise<T> resolved) throws Exception {
+ T value;
+ try {
+ value = resolved.getValue();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return null;
+ }
+ chained.resolve(value, null);
+ return null;
+ }
+
+ public void fail(Promise<?> resolved) throws Exception {
+ Promise<? extends T> recovered;
+ Throwable failure;
+ try {
+ recovered = recovery.apply(resolved);
+ failure = resolved.getFailure();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return;
+ }
+ if (recovered == null) {
+ chained.resolve(null, failure);
+ } else {
+ recovered.onResolve(new Chain<T>(chained, recovered));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Promise<T> fallbackTo(Promise<? extends T> fallback) {
+ PromiseImpl<T> chained = new PromiseImpl<T>();
+ FallbackTo<T> fallbackTo = new FallbackTo<T>(chained, fallback);
+ then(fallbackTo, fallbackTo);
+ return chained;
+ }
+
+ /**
+ * A callback used by the {@link PromiseImpl#fallbackTo(Promise)} method.
+ *
+ * @Immutable
+ */
+ private static final class FallbackTo<T> implements Success<T, Void>, Failure {
+ private final PromiseImpl<T> chained;
+ private final Promise<? extends T> fallback;
+
+ FallbackTo(PromiseImpl<T> chained, Promise<? extends T> fallback) {
+ this.chained = chained;
+ this.fallback = requireNonNull(fallback);
+ }
+
+ public Promise<Void> call(Promise<T> resolved) throws Exception {
+ T value;
+ try {
+ value = resolved.getValue();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return null;
+ }
+ chained.resolve(value, null);
+ return null;
+ }
+
+ public void fail(Promise<?> resolved) throws Exception {
+ Throwable failure;
+ try {
+ failure = resolved.getFailure();
+ } catch (Throwable e) {
+ chained.resolve(null, e);
+ return;
+ }
+ fallback.onResolve(new Chain<T>(chained, fallback, failure));
+ }
+ }
+
+ static <V> V requireNonNull(V value) {
+ if (value != null) {
+ return value;
+ }
+ throw new NullPointerException();
+ }
+
+ /**
+ * Use the lazy initialization holder class idiom to delay creating a Logger
+ * until we actually need it.
+ */
+ private static final class Logger {
+ private final static java.util.logging.Logger LOGGER;
+ static {
+ LOGGER = java.util.logging.Logger.getLogger(PromiseImpl.class.getName());
+ }
+
+ static void logCallbackException(Throwable t) {
+ LOGGER.log(java.util.logging.Level.WARNING, "Exception from Promise callback", t);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java
new file mode 100644
index 000000000..06d506675
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java
@@ -0,0 +1,180 @@
+/*
+ * 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$
+ */
+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));
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java
new file mode 100644
index 000000000..c29fc4fb6
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Success callback for a Promise.
+ *
+ * <p>
+ * A Success callback is registered with a {@link Promise} using the
+ * {@link Promise#then(Success)} method and is called if the Promise is resolved
+ * successfully.
+ *
+ * <p>
+ * This is a functional interface and can be used as the assignment target for a
+ * lambda expression or method reference.
+ *
+ * @param <T> The value type of the resolved Promise passed as input to this
+ * callback.
+ * @param <R> The value type of the returned Promise from this callback.
+ *
+ * @ThreadSafe
+ * @author $Id$
+ */
+@ConsumerType
+public interface Success<T, R> {
+ /**
+ * Success callback for a Promise.
+ *
+ * <p>
+ * This method is called if the Promise with which it is registered resolves
+ * successfully.
+ *
+ * <p>
+ * In the remainder of this description we will refer to the Promise
+ * returned by this method as the returned Promise and the Promise returned
+ * by {@link Promise#then(Success)} when this Success callback was
+ * registered as the chained Promise.
+ *
+ * <p>
+ * If the returned Promise is {@code null} then the chained Promise must
+ * resolve immediately with a successful value of {@code null}. If the
+ * returned Promise is not {@code null} then the chained Promise must be
+ * resolved when the returned Promise is resolved.
+ *
+ * @param resolved The successfully resolved {@link Promise}.
+ * @return The Promise to use to resolve the chained Promise, or
+ * {@code null} if the chained Promise is to be resolved immediately
+ * with the value {@code null}.
+ * @throws Exception The chained Promise must be failed with the thrown
+ * exception.
+ */
+ Promise<R> call(Promise<T> resolved) throws Exception;
+}
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java
new file mode 100644
index 000000000..5a3ec65d3
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * Promise Package Version 1.0.
+ *
+ * <p>
+ * Bundles wishing to use this package must list the package in the
+ * Import-Package header of the bundle's manifest.
+ *
+ * <p>
+ * Example import for consumers using the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.util.promise; version="[1.0,2.0)"}
+ * <p>
+ * Example import for providers implementing the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.util.promise; version="[1.0,1.1)"}
+ *
+ * @author $Id$
+ */
+
+@Version("1.0")
+package org.osgi.util.promise;
+
+import org.osgi.annotation.versioning.Version;
+
diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo
new file mode 100644
index 000000000..7c8de0324
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo
@@ -0,0 +1 @@
+version 1.0

Back to the top