Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promise.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promise.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promise.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promise.java Wed Jul 8 22:10:14 2015 @@ -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<String> foo = foo(); + * foo.onResolve(new Runnable() { + * public void run() { + * System.out.println(foo.getValue()); + * } + * }); + * </pre> + * + * Example chaining usage; + * + * <pre> + * Success<String,String> doubler = new Success<String,String>() { + * public Promise<String> call(Promise<String> p) throws Exception { + * return Promises.resolved(p.getValue()+p.getValue()); + * } + * }; + * final Promise<String> 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: 8a24f66339967ecdd03def57f31ac8c371a7f4cd $ + */ +@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 will + * 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 will be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise will 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 will 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 will be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise will 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 will map the value of this Promise to the + * value that will 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 will 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 will be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise will 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 will flatMap the value of this Promise to + * a Promise that will 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 will 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 will + * be resolved with the recovery value.</li> + * <li>If the recovery value is {@code null}, the returned Promise will be + * failed with the failure of this Promise.</li> + * <li>If the specified Function throws an exception, the returned Promise + * will 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 will 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 + * will be resolved with the recovery Promise.</li> + * <li>If the recovery Promise is {@code null}, the returned Promise will be + * failed with the failure of this Promise.</li> + * <li>If the specified Function throws an exception, the returned Promise + * will 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 will 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 will + * 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 will 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); +}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/PromiseImpl.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/PromiseImpl.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/PromiseImpl.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/PromiseImpl.java Wed Jul 8 22:10:14 2015 @@ -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: d8b44a36f3eb797316b213118192fac213fa0c59 $ + */ +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 will 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 will be successfully + * resolved, with the value {@code null}, if this Promise was + * resolved by the specified Promise. The returned Promise will 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); + } + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promises.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promises.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promises.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Promises.java Wed Jul 8 22:10:14 2015 @@ -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: 6ad9c7cca5a98dbddd87db8417df51bf6e008922 $ + */ +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 will 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 will 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 will 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 will 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)); + } + } + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Success.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Success.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Success.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/Success.java Wed Jul 8 22:10:14 2015 @@ -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: 58eef5ba732ef999d57a1feaaf1e5229356647e3 $ + */ +@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 will + * resolve immediately with a successful value of {@code null}. If the + * returned Promise is not {@code null} then the chained Promise will 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 will be failed with the thrown + * exception. + */ + Promise<R> call(Promise<T> resolved) throws Exception; +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/package-info.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/package-info.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/package-info.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/osgi/util/promise/package-info.java Wed Jul 8 22:10:14 2015 @@ -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: 5a3ec65d3b7e7ebdd2278d75675b8a808e6cb2bf $ + */ + +@Version("1.0") +package org.osgi.util.promise; + +import org.osgi.annotation.versioning.Version; + Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/.gitignore URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/.gitignore?rev=1689973&view=auto ============================================================================== (empty) Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/DefaultPackageClass.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/DefaultPackageClass.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/DefaultPackageClass.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/DefaultPackageClass.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * The DefaultPackageClass is just present for the + * {@link org.apache.felix.scr.impl.ReflectionHelperTest} to be able to test + * the <code>ReflectionHelper.getPackageName</code> method. + */ +public class DefaultPackageClass +{ + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_10.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_10.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_10.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_11.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_11.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_activate_11.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.properties URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.properties?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.properties (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.properties Wed Jul 8 22:10:14 2015 @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +# Sample properties file for the metadate load test + +file.property = Property from File \ No newline at end of file Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_all_elements_10.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_10.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_10.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_10.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_11.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_11.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_anonymous_11.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_10.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_10.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_10.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_11.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_11.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_implementation_11.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_10.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_10.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_10.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_11.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_11.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_duplicate_service_11.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_no_namespace.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_no_namespace.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_no_namespace.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_properties_11.xml URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_properties_11.xml?rev=1689973&view=auto ============================================================================== Binary file - no diff available. Propchange: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/components_properties_11.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/BundleComponentActivatorTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/BundleComponentActivatorTest.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/BundleComponentActivatorTest.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/BundleComponentActivatorTest.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Vector; + +import junit.framework.TestCase; + +import org.easymock.EasyMock; +import org.osgi.framework.Bundle; + +public class BundleComponentActivatorTest extends TestCase +{ + + /** + * Test that an empty array is returned for a null bundle. + */ + public void test_findDescriptors_withNullBundle() + { + final URL[] urls = BundleComponentActivator.findDescriptors( null, "foo.xml" ); + assertNotNull( "Descriptor array is not null", urls ); + assertEquals( "Descriptor array length", 0, urls.length ); + } + + /** + * Test that an empty array is returned for a null location. + */ + public void test_findDescriptors_withNullLocation() + { + final URL[] urls = BundleComponentActivator.findDescriptors( new MockBundle(), null ); + assertNotNull( "Descriptor array is not null", urls ); + assertEquals( "Descriptor array length", 0, urls.length ); + } + + /** + * Test that an empty array is returned for an empty location. + */ + public void test_findDescriptors_withEmptyLocation() + { + final URL[] urls = BundleComponentActivator.findDescriptors( new MockBundle(), "" ); + assertNotNull( "Descriptor array is not null", urls ); + assertEquals( "Descriptor array length", 0, urls.length ); + } + + /** + * Test that an empty array is returned for a location containing only blanks. + */ + public void test_findDescriptors_withBlankLocation() + { + final URL[] urls = BundleComponentActivator.findDescriptors( new MockBundle(), " " ); + assertNotNull( "Descriptor array is not null", urls ); + assertEquals( "Descriptor array length", 0, urls.length ); + } + + /** + * Test that when using a non wilcarded location, getResource() will be used (for legacy reasons) and the returned + * array is the one returned by bundle method call. + * + * @throws MalformedURLException unexpected + */ + public void test_findDescriptors_withNonWildcardLocation() throws MalformedURLException + { + final URL[] descriptors = new URL[] + { new URL( "file:foo.xml" ) }; + final Enumeration de = new Vector( Arrays.asList( descriptors ) ).elements(); + final Bundle bundle = ( Bundle ) EasyMock.createNiceMock( Bundle.class ); + EasyMock.expect( bundle.findEntries( "/some/location", "foo.xml", false ) ).andReturn( de ); + + EasyMock.replay( new Object[] + { bundle } ); + final URL[] urls = BundleComponentActivator.findDescriptors( bundle, "/some/location/foo.xml" ); + EasyMock.verify( new Object[] + { bundle } ); + + assertNotNull( "Descriptor array is not null", urls ); + assertEquals( "Descriptor length", 1, urls.length ); + assertEquals( "Descriptor", descriptors[0], urls[0] ); + } + + + public void findDescriptors_withWildcardLocation( final String location, + final String path, + final String filePattern ) + throws MalformedURLException + { + final URL[] urls = new URL[] + { + new URL( "file:foo1.xml" ), + new URL( "file:foo2.xml" ) + }; + final Enumeration de = new Vector( Arrays.asList( urls ) ).elements(); + final Bundle bundle = (Bundle) EasyMock.createNiceMock( Bundle.class ); + EasyMock.expect( bundle.findEntries( path, filePattern, false ) ).andReturn( de ); + + EasyMock.replay( new Object[]{ bundle } ); + final URL[] actualUrls = BundleComponentActivator.findDescriptors( bundle, location ); + EasyMock.verify( new Object[]{ bundle } ); + + assertNotNull( "Descriptor array is not null", actualUrls ); + assertEquals( "Descriptor length", urls.length, actualUrls.length ); + for( int i = 0; i < actualUrls.length; i++ ) + { + assertEquals( "Descriptor", urls[ i ], actualUrls[ i ] ); + } + } + + /** + * Test that when using "*.xml", path will be root of bundle "/" and file pattern will be "*.xml". + * + * @throws MalformedURLException unexpected + */ + public void test_findDescriptors_withWildcardLocation01() + throws MalformedURLException + { + findDescriptors_withWildcardLocation( "*.xml", "/", "*.xml" ); + } + + /** + * Test that when using "/foo/*.xml", path will be "/foo/" and file pattern will be "*.xml". + * + * @throws MalformedURLException unexpected + */ + public void test_findDescriptors_withWildcardLocation02() + throws MalformedURLException + { + findDescriptors_withWildcardLocation( "/foo/*.xml", "/foo", "*.xml" ); + } + + /** + * Test that in case that no resources are found (bundle return null) an empty array is returned. + * + * @throws MalformedURLException unexpected + */ + public void test_findDescriptors_withWildcardLocation_nullEnum() + throws MalformedURLException + { + final Bundle bundle = (Bundle) EasyMock.createNiceMock( Bundle.class ); + EasyMock.expect( bundle.findEntries( "/", "*.xml", false ) ).andReturn( null ); + + EasyMock.replay( new Object[]{ bundle } ); + final URL[] actualUrls = BundleComponentActivator.findDescriptors( bundle, "*.xml" ); + EasyMock.verify( new Object[]{ bundle } ); + + assertNotNull( "Descriptor array is not null", actualUrls ); + assertEquals( "Descriptor length", 0, actualUrls.length ); + } + + /** + * Test that in case that no resources are found (bundle return empty enum) an empty array is returned. + * + * @throws MalformedURLException unexpected + */ + public void test_findDescriptors_withWildcardLocation_emptyEnum() + throws MalformedURLException + { + final Bundle bundle = (Bundle) EasyMock.createNiceMock( Bundle.class ); + EasyMock.expect( bundle.findEntries( "/", "*.xml", false ) ).andReturn( new Vector().elements() ); + + EasyMock.replay( new Object[]{ bundle } ); + final URL[] actualUrls = BundleComponentActivator.findDescriptors( bundle, "*.xml" ); + EasyMock.verify( new Object[]{ bundle } ); + + assertNotNull( "Descriptor array is not null", actualUrls ); + assertEquals( "Descriptor length", 0, actualUrls.length ); + } + +} \ No newline at end of file Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/ComponentRegistryKeyTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/ComponentRegistryKeyTest.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/ComponentRegistryKeyTest.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/ComponentRegistryKeyTest.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl; + + +import junit.framework.TestCase; + + +public class ComponentRegistryKeyTest extends TestCase +{ + + private final ComponentRegistryKey b1_a_0 = key( 1, "a" ); + private final ComponentRegistryKey b2_a_0 = key( 2, "a" ); + + private final ComponentRegistryKey b1_a_1 = key( 1, "a" ); + private final ComponentRegistryKey b2_a_1 = key( 2, "a" ); + + private final ComponentRegistryKey b1_b = key( 1, "b" ); + private final ComponentRegistryKey b2_b = key( 2, "b" ); + + + public void test_globally_unique_key() + { + // same + TestCase.assertEquals( b1_a_0, b1_a_0 ); + + // equals both ways + TestCase.assertEquals( b1_a_0, b1_a_1 ); + TestCase.assertEquals( b1_a_1, b1_a_0 ); + + // not equals both ways + TestCase.assertFalse( b1_a_0.equals( b2_a_0 ) ); + TestCase.assertFalse( b2_a_0.equals( b1_a_0 ) ); + + // not equals both ways + TestCase.assertFalse( b1_a_0.equals( b1_b ) ); + TestCase.assertFalse( b1_b.equals( b1_a_0 ) ); + + // not equals both ways + TestCase.assertFalse( b1_a_0.equals( b2_b ) ); + TestCase.assertFalse( b2_b.equals( b1_a_0 ) ); + } + + + private static ComponentRegistryKey key( final long bundleId, final String name ) + { + return new ComponentRegistryKey( new MockBundle() + { +// @Override + public long getBundleId() + { + return bundleId; + } + }, name ); + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockBundle.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockBundle.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockBundle.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockBundle.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl; + + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.security.cert.X509Certificate; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; + + +public class MockBundle implements Bundle +{ + + public Enumeration findEntries( String arg0, String arg1, boolean arg2 ) + { + return null; + } + + + public BundleContext getBundleContext() + { + return null; + } + + public Map<X509Certificate, List<X509Certificate>> getSignerCertificates( int i ) + { + return null; + } + + public Version getVersion() + { + return null; + } + + public <A> A adapt( Class<A> aClass ) + { + return null; + } + + public File getDataFile( String s ) + { + return null; + } + + + public long getBundleId() + { + return 0; + } + + + public URL getEntry( String name ) + { + return getClass().getClassLoader().getResource( name ); + } + + + public Enumeration getEntryPaths( String arg0 ) + { + return null; + } + + + public Dictionary getHeaders() + { + return null; + } + + + public Dictionary getHeaders( String arg0 ) + { + return null; + } + + + public long getLastModified() + { + return 0; + } + + + public String getLocation() + { + return "test:mockbundle"; + } + + + public ServiceReference[] getRegisteredServices() + { + return null; + } + + + public URL getResource( String arg0 ) + { + return null; + } + + + public Enumeration getResources( String arg0 ) + { + return null; + } + + + public ServiceReference[] getServicesInUse() + { + return null; + } + + + public int getState() + { + return 0; + } + + + public String getSymbolicName() + { + return null; + } + + + public boolean hasPermission( Object arg0 ) + { + return false; + } + + + public Class loadClass( String arg0 ) + { + return null; + } + + + public void start() + { + + } + + + public void start( int options ) + { + + } + + + public void stop() + { + + } + + + public void stop( int options ) + { + + } + + + public void uninstall() + { + + } + + + public void update() + { + + } + + + public void update( InputStream arg0 ) + { + + } + + public int compareTo( Bundle bundle ) + { + return 0; + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockLogger.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockLogger.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockLogger.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/MockLogger.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl; + + +import java.text.MessageFormat; + +import org.apache.felix.scr.impl.helper.Logger; +import org.apache.felix.scr.impl.metadata.ComponentMetadata; + + +public class MockLogger implements Logger +{ + String lastMessage; + + + public boolean isLogEnabled( int level ) + { + return true; + } + + + public void log( int level, String pattern, Object[] arguments, ComponentMetadata metadata, Long componentId, Throwable ex ) + { + if ( isLogEnabled( level ) ) + { + log( level, MessageFormat.format( pattern, arguments ), metadata, null, ex ); + } + } + + + public void log( int level, String message, ComponentMetadata metadata, Long componentId, Throwable ex ) + { + lastMessage = message; + } + + + public boolean messageContains( String value ) + { + return lastMessage != null && lastMessage.indexOf( value ) >= 0; + } +} \ No newline at end of file Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/SCRCommandlTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/SCRCommandlTest.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/SCRCommandlTest.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/SCRCommandlTest.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; + +import org.osgi.dto.DTO; +import org.osgi.framework.dto.BundleDTO; +import org.osgi.framework.dto.ServiceReferenceDTO; + +import junit.framework.TestCase; + +public class SCRCommandlTest extends TestCase +{ + + + public void testPropertyInfo() + { + ScrCommand scr = new ScrCommand(null, null, null); + check(scr, String.format(" Properties:%n key = [1, 2]%n"), new int[] {1, 2}); + check(scr, String.format(" Properties:%n key = [1, 2]%n"), new String[] {"1", "2"}); + check(scr, String.format(" Properties:%n key = [true, false]%n"), new Boolean[] {true, false}); + check(scr, String.format(" Properties:%n key = foo%n"), "foo"); + check(scr, String.format(" Properties:%n key = true%n"), true); + } + + private PrintWriter check(ScrCommand scr, String expected, Object o) + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + scr.propertyInfo(Collections.<String, Object>singletonMap("key", o), pw, ""); + assertEquals(expected, sw.toString()); + return pw; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/config/ConfigurationSupportTest.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/config/ConfigurationSupportTest.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/config/ConfigurationSupportTest.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/test/org/apache/felix/scr/impl/config/ConfigurationSupportTest.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.felix.scr.impl.config; + +import junit.framework.TestCase; + +public class ConfigurationSupportTest extends TestCase +{ + + public void testEscape() + { + assertEquals("foo \\(&\\)", ConfigurationSupport.escape("foo (&)")); + } + +}
