Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleAdapterBuilderImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleAdapterBuilderImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleAdapterBuilderImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleAdapterBuilderImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,260 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.dm.lambda.BundleAdapterBuilder; +import org.apache.felix.dm.lambda.ComponentBuilder; +import org.apache.felix.dm.lambda.callbacks.CbBundle; +import org.apache.felix.dm.lambda.callbacks.CbComponentBundle; +import org.apache.felix.dm.lambda.callbacks.CbTypeBundle; +import org.apache.felix.dm.lambda.callbacks.CbTypeComponentBundle; +import org.osgi.framework.Bundle; + +public class BundleAdapterBuilderImpl implements AdapterBase<BundleAdapterBuilder>, BundleAdapterBuilder { + private Consumer<ComponentBuilder<?>> m_compBuilder = (compBuilder -> {}); + protected final Map<Cb, List<MethodRef<Object>>> m_refs = new HashMap<>(); + private DependencyManager m_dm; + private boolean m_autoAdd; + private String m_added; + private String m_changed; + private String m_removed; + private String m_filter; + private int m_stateMask = -1; + private boolean m_propagate; + private Object m_callbackInstance; + private String m_add; + private String m_change; + private String m_remove; + + enum Cb { + ADD, + CHG, + REM + }; + + @FunctionalInterface + interface MethodRef<I> { + public void accept(I instance, Component c, Bundle b); + } + + public BundleAdapterBuilderImpl(DependencyManager dm) { + m_dm = dm; + } + + public void andThenBuild(Consumer<ComponentBuilder<?>> builder) { + m_compBuilder = m_compBuilder.andThen(builder); + } + + @Override + public BundleAdapterBuilderImpl autoAdd(boolean autoAdd) { + m_autoAdd = autoAdd; + return this; + } + + public boolean isAutoAdd() { + return m_autoAdd; + } + + public BundleAdapterBuilder mask(int mask) { + m_stateMask = mask; + return this; + } + + public BundleAdapterBuilder filter(String filter) { + m_filter = filter; + return this; + } + + public BundleAdapterBuilder propagate(boolean propagate) { + m_propagate = propagate; + return this; + } + + public BundleAdapterBuilder propagate() { + m_propagate = true; + return this; + } + + public BundleAdapterBuilder cb(String ... callbacks) { + return cbi(null, callbacks); + } + + public BundleAdapterBuilder cbi(Object callbackInstance, String ... callbacks) { + switch (callbacks.length) { + case 1: + return cbi(callbackInstance, callbacks[0], null, null); + + case 2: + return cbi(callbackInstance, callbacks[0], null, callbacks[1]); + + case 3: + return cbi(callbackInstance, callbacks[0], callbacks[1], callbacks[2]); + + default: + throw new IllegalArgumentException("wrong number of arguments: " + callbacks.length + ". " + + "Possible arguments: [add], [add, remove] or [add, change, remove]"); + } + } + + private BundleAdapterBuilder cbi(Object callbackInstance, String add, String change, String remove) { + checkHasNoMethodRefs(); + m_callbackInstance = callbackInstance; + m_add = add; + m_change = change; + m_remove = remove; + return this; + } + + public <T> BundleAdapterBuilder cb(CbTypeBundle<T> add) { + return cb(add, (CbTypeBundle<T>) null, (CbTypeBundle<T>) null); + } + + public <T> BundleAdapterBuilder cb(CbTypeBundle<T> add, CbTypeBundle<T> remove) { + return cb(add, null, remove); + } + + public <T> BundleAdapterBuilder cb(CbTypeBundle<T> add, CbTypeBundle<T> change, CbTypeBundle<T> remove) { + if (add != null) { + Class<T> type = Helpers.getLambdaArgType(add, 0); + setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { add.accept((T) instance, bundle); }); + } + if (change != null) { + Class<T> type = Helpers.getLambdaArgType(change, 0); + setComponentCallbackRef(Cb.CHG, type, (instance, component, bundle) -> { change.accept((T) instance, bundle); }); + } + if (remove != null) { + Class<T> type = Helpers.getLambdaArgType(remove, 0); + setComponentCallbackRef(Cb.REM, type, (instance, component, bundle) -> { remove.accept((T) instance, bundle); }); + } + return this; + } + + public BundleAdapterBuilder cbi(CbBundle add) { + return cbi(add, null, null); + } + + public BundleAdapterBuilder cbi(CbBundle add, CbBundle remove) { + return cbi(add, null, remove); + } + + public BundleAdapterBuilder cbi(CbBundle add, CbBundle change, CbBundle remove) { + if (add != null) setInstanceCallbackRef(Cb.ADD, (instance, component, bundle) -> { add.accept(bundle); }); + if (change != null) setInstanceCallbackRef(Cb.CHG, (instance, component, bundle) -> { change.accept(bundle); }); + if (remove != null) setInstanceCallbackRef(Cb.REM, (instance, component, bundle) -> { remove.accept(bundle); }); + return this; + } + + public <T> BundleAdapterBuilder cb(CbTypeComponentBundle<T> add) { + return cb((CbTypeComponentBundle<T>) add, (CbTypeComponentBundle<T>) null, (CbTypeComponentBundle<T>) null); + } + + public <T> BundleAdapterBuilder cb(CbTypeComponentBundle<T> add, CbTypeComponentBundle<T> remove) { + return cb(add, null, remove); + } + + public <T> BundleAdapterBuilder cb(CbTypeComponentBundle<T> add, CbTypeComponentBundle<T> change, CbTypeComponentBundle<T> remove) { + if (add != null) { + Class<T> type = Helpers.getLambdaArgType(add, 0); + return setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { add.accept((T) instance, component, bundle); }); + } + if (change != null) { + Class<T> type = Helpers.getLambdaArgType(change, 0); + return setComponentCallbackRef(Cb.CHG, type, (instance, component, bundle) -> { change.accept((T) instance, component, bundle); }); + } + if (remove != null) { + Class<T> type = Helpers.getLambdaArgType(remove, 0); + return setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { remove.accept((T) instance, component, bundle); }); + } + return this; + } + + public BundleAdapterBuilder cbi(CbComponentBundle add) { + return cbi(add, null, null); + } + + public BundleAdapterBuilder cbi(CbComponentBundle add, CbComponentBundle remove) { + return cbi(add, null, remove); + } + + public BundleAdapterBuilder cbi(CbComponentBundle add, CbComponentBundle change, CbComponentBundle remove) { + if (add != null) setInstanceCallbackRef(Cb.ADD, (instance, component, bundle) -> { add.accept(component, bundle); }); + if (change != null) setInstanceCallbackRef(Cb.CHG, (instance, component, bundle) -> { change.accept(component, bundle); }); + if (remove != null) setInstanceCallbackRef(Cb.REM, (instance, component, bundle) -> { remove.accept(component, bundle); }); + return this; + } + + @Override + public Component build() { + Component c = null; + + if (m_refs.size() > 0) { + @SuppressWarnings("unused") + Object wrapCallback = new Object() { + public void add(Component comp, Bundle bundle) { + invokeMethodRefs(Cb.ADD, comp, bundle); + } + + public void change(Component comp, Bundle bundle) { + invokeMethodRefs(Cb.CHG, comp, bundle); + } + + public void remove(Component comp, Bundle bundle) { + invokeMethodRefs(Cb.REM, comp, bundle); + } + }; + c = m_dm.createBundleAdapterService(m_stateMask, m_filter, m_propagate, wrapCallback, "add", "change", "remove"); + } else { + c = m_dm.createBundleAdapterService(m_stateMask, m_filter, m_propagate, m_callbackInstance, m_add, m_change, m_remove); + } + ComponentBuilderImpl cb = new ComponentBuilderImpl(c, false); + m_compBuilder.accept (cb); + return cb.build(); + } + + private <U> BundleAdapterBuilder setInstanceCallbackRef(Cb cbType, MethodRef<U> ref) { + checkHasNoReflectionCallbacks(); + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component, bundle) -> ref.accept(null, component, bundle)); + return this; + } + + @SuppressWarnings("unchecked") + private <U> BundleAdapterBuilder setComponentCallbackRef(Cb cbType, Class<U> type, MethodRef<U> ref) { + checkHasNoReflectionCallbacks(); + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component, bundle) -> { + Object componentImpl = Stream.of(component.getInstances()) + .filter(impl -> Helpers.getClass(impl).equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes.")); + ref.accept((U) componentImpl, component, bundle); + }); + return this; + } + + private void invokeMethodRefs(Cb cbType, Component comp, Bundle bundle) { + m_refs.computeIfPresent(cbType, (k, mrefs) -> { + mrefs.forEach(mref -> mref.accept(null, comp, bundle)); + return mrefs; + }); + } + + private void checkHasNoMethodRefs() { + if (m_refs.size() > 0) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } + + private void checkHasNoReflectionCallbacks() { + if (m_added != null || m_changed != null || m_removed != null) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } +}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleDependencyBuilderImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleDependencyBuilderImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleDependencyBuilderImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/BundleDependencyBuilderImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,359 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.apache.felix.dm.BundleDependency; +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.dm.lambda.BundleDependencyBuilder; +import org.apache.felix.dm.lambda.callbacks.CbBundle; +import org.apache.felix.dm.lambda.callbacks.CbComponentBundle; +import org.apache.felix.dm.lambda.callbacks.CbTypeBundle; +import org.apache.felix.dm.lambda.callbacks.CbTypeComponentBundle; +import org.osgi.framework.Bundle; + +@SuppressWarnings("unchecked") +public class BundleDependencyBuilderImpl implements BundleDependencyBuilder { + private String m_added; + private String m_changed; + private String m_removed; + private Object m_instance; + private boolean m_autoConfig = true; + private boolean m_autoConfigInvoked = false; + private boolean m_required = true; + private Bundle m_bundle; + private String m_filter; + private int m_stateMask = -1; + private boolean m_propagate; + private Object m_propagateInstance; + private String m_propagateMethod; + private Supplier<Dictionary<?, ?>> m_propagateSupplier; + private final Component m_component; + + enum Cb { + ADD, + CHG, + REM + }; + + private final Map<Cb, List<MethodRef<Object>>> m_refs = new HashMap<>(); + + @FunctionalInterface + interface MethodRef<I> { + public void accept(I instance, Component c, Bundle bundle); + } + + /** + * Class used to call a supplier that returns Propagated properties + */ + private class Propagate { + @SuppressWarnings("unused") + Dictionary<?, ?> propagate() { + return m_propagateSupplier.get(); + } + } + + public BundleDependencyBuilderImpl (Component component) { + m_component = component; + } + + @Override + public BundleDependencyBuilder autoConfig(boolean autoConfig) { + m_autoConfig = autoConfig; + m_autoConfigInvoked = true; + return this; + } + + @Override + public BundleDependencyBuilder autoConfig() { + autoConfig(true); + return this; + } + + @Override + public BundleDependencyBuilder required(boolean required) { + m_required = required; + return this; + } + + @Override + public BundleDependencyBuilder required() { + required(true); + return this; + } + + @Override + public BundleDependencyBuilder bundle(Bundle bundle) { + m_bundle = bundle; + return this; + } + + @Override + public BundleDependencyBuilder filter(String filter) throws IllegalArgumentException { + m_filter = filter; + return this; + } + + @Override + public BundleDependencyBuilder mask(int mask) { + m_stateMask = mask; + return this; + } + + @Override + public BundleDependencyBuilder propagate(boolean propagate) { + m_propagate = propagate; + return this; + } + + @Override + public BundleDependencyBuilder propagate() { + propagate(true); + return this; + } + + @Override + public BundleDependencyBuilder propagate(Object instance, String method) { + if (m_propagateSupplier != null || m_propagate) throw new IllegalStateException("Propagate callback already set."); + Objects.nonNull(method); + Objects.nonNull(instance); + m_propagateInstance = instance; + m_propagateMethod = method; + return this; + } + + @Override + public BundleDependencyBuilder propagate(Supplier<Dictionary<?, ?>> instance) { + if (m_propagateInstance != null || m_propagate) throw new IllegalStateException("Propagate callback already set."); + m_propagateSupplier = instance; + return this; + } + + public BundleDependencyBuilder cb(String ... callbacks) { + return cb(null, callbacks); + } + + @Override + public BundleDependencyBuilder cb(Object callbackInstance, String ... callbacks) { + switch (callbacks.length) { + case 1: + cbi(callbackInstance, callbacks[0], null, null); + break; + + case 2: + cbi(callbackInstance, callbacks[0], null, callbacks[1]); + break; + + case 3: + cbi(callbackInstance, callbacks[0], callbacks[1], callbacks[2]); + break; + + default: + throw new IllegalArgumentException("wrong number of arguments: " + callbacks.length + ". " + + "Possible arguments: [add], [add, remove] or [add, change, remove]"); + } + + return this; + } + + private BundleDependencyBuilder cbi(Object callbackInstance, String added, String changed, String removed) { + requiresNoMethodRefs(); + m_instance = callbackInstance; + m_added = added != null ? added : m_added; + m_changed = changed != null ? changed : m_changed; + m_removed = removed != null ? removed : m_removed; + if (! m_autoConfigInvoked) m_autoConfig = false; + return this; + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeBundle<T> add) { + return cb(add, null, null); + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeBundle<T> add, CbTypeBundle<T> remove) { + return cb(add, null, remove); + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeBundle<T> add, CbTypeBundle<T> change, CbTypeBundle<T> remove) { + if (add != null) { + setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, bundle)); + } + if (change != null) { + setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, bundle)); + } + if (remove != null) { + setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, bundle)); + } + return this; + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeComponentBundle<T> add) { + return cb(add, null, null); + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeComponentBundle<T> add, CbTypeComponentBundle<T> remove) { + return cb(add, null, remove); + } + + @Override + public <T> BundleDependencyBuilder cb(CbTypeComponentBundle<T> add, CbTypeComponentBundle<T> change, CbTypeComponentBundle<T> remove) { + if (add != null) { + setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, component, bundle)); + } + if (change != null) { + setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, component, bundle)); + } + if (remove != null) { + setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, component, bundle)); + } + return this; + } + + @Override + public BundleDependencyBuilder cbi(CbBundle add) { + return cbi(add, null, null); + } + + @Override + public BundleDependencyBuilder cbi(CbBundle add, CbBundle remove) { + return cbi(add, null, remove); + } + + @Override + public BundleDependencyBuilder cbi(CbBundle add, CbBundle change, CbBundle remove) { + if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(bundle)); + if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(bundle)); + if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(bundle)); + return this; + } + + @Override + public BundleDependencyBuilder cbi(CbComponentBundle add) { + return cbi(add, null, null); + } + + @Override + public BundleDependencyBuilder cbi(CbComponentBundle add, CbComponentBundle remove) { + return cbi(add, null, remove); + } + + @Override + public BundleDependencyBuilder cbi(CbComponentBundle add, CbComponentBundle change, CbComponentBundle remove) { + if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(component, bundle)); + if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(component, bundle)); + if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(component, bundle)); + return this; + } + + @Override + public BundleDependency build() { + DependencyManager dm = m_component.getDependencyManager(); + + BundleDependency dep = dm.createBundleDependency(); + dep.setRequired(m_required); + + if (m_filter != null) { + dep.setFilter(m_filter); + } + + if (m_bundle != null) { + dep.setBundle(m_bundle); + } + + if (m_stateMask != -1) { + dep.setStateMask(m_stateMask); + } + + if (m_propagate) { + dep.setPropagate(true); + } else if (m_propagateInstance != null) { + dep.setPropagate(m_propagateInstance, m_propagateMethod); + } else if (m_propagateSupplier != null) { + dep.setPropagate(new Propagate(), "propagate"); + } + + if (m_added != null || m_changed != null || m_removed != null) { + dep.setCallbacks(m_instance, m_added, m_changed, m_removed); + } else if (m_refs.size() > 0) { + Object cb = createCallbackInstance(); + dep.setCallbacks(cb, "add", "change", "remove"); + } + + dep.setAutoConfig(m_autoConfig); + return dep; + } + + private <T> BundleDependencyBuilder setInstanceCallbackRef(Cb cbType, MethodRef<T> ref) { + requiresNoStringCallbacks(); + if (! m_autoConfigInvoked) m_autoConfig = false; + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component, bundle) -> ref.accept(null, component, bundle)); + return this; + } + + private <T> BundleDependencyBuilder setComponentCallbackRef(Cb cbType, Class<T> type, MethodRef<T> ref) { + requiresNoStringCallbacks(); + if (! m_autoConfigInvoked) m_autoConfig = false; + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component, bundle) -> { + Object componentImpl = Stream.of(component.getInstances()) + .filter(impl -> Helpers.getClass(impl).equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes.")); + ref.accept((T) componentImpl, component, bundle); + }); + return this; + } + + @SuppressWarnings("unused") + private Object createCallbackInstance() { + Object cb = null; + + cb = new Object() { + void add(Component c, Bundle bundle) { + invokeMethodRefs(Cb.ADD, c, bundle); + } + + void change(Component c, Bundle bundle) { + invokeMethodRefs(Cb.CHG, c, bundle); + } + + void remove(Component c, Bundle bundle) { + invokeMethodRefs(Cb.REM, c, bundle); + } + }; + + return cb; + } + + private void invokeMethodRefs(Cb cbType, Component c, Bundle bundle) { + m_refs.computeIfPresent(cbType, (k, mrefs) -> { + mrefs.forEach(mref -> mref.accept(null, c, bundle)); + return mrefs; + }); + } + + private void requiresNoStringCallbacks() { + if (m_added != null || m_changed != null || m_removed != null) { + throw new IllegalStateException("can't mix method references and string callbacks."); + } + } + + private void requiresNoMethodRefs() { + if (m_refs.size() > 0) { + throw new IllegalStateException("can't mix method references and string callbacks."); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/CompletableFutureDependencyImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/CompletableFutureDependencyImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/CompletableFutureDependencyImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/CompletableFutureDependencyImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,241 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.Dependency; +import org.apache.felix.dm.context.AbstractDependency; +import org.apache.felix.dm.context.DependencyContext; +import org.apache.felix.dm.context.Event; +import org.apache.felix.dm.context.EventType; +import org.apache.felix.dm.lambda.FutureDependencyBuilder; +import org.apache.felix.dm.lambda.callbacks.CbFuture; +import org.apache.felix.dm.lambda.callbacks.CbTypeFuture; +import org.osgi.service.log.LogService; + +public class CompletableFutureDependencyImpl<F> extends AbstractDependency<CompletableFutureDependencyImpl<F>> implements FutureDependencyBuilder<F> { + + private final CompletableFuture<F> m_future; + private Component m_comp; + private boolean m_async; + private Executor m_exec; + private CbFuture<F> m_accept = (future) -> {}; + private CbTypeFuture<Object, F> m_accept2; + private Class<?> m_accept2Type; + + public CompletableFutureDependencyImpl(Component c, CompletableFuture<F> future) { + super.setRequired(true); + m_future = future; + m_comp = c; + } + + /** + * Create a new PathDependency from an existing prototype. + * + * @param prototype + * the existing PathDependency. + */ + public CompletableFutureDependencyImpl(Component component, CompletableFutureDependencyImpl<F> prototype) { + super(prototype); + m_future = prototype.m_future; + m_comp = component; + } + + @Override + public Dependency build() { + return this; + } + + @Override + public FutureDependencyBuilder<F> cb(String callback) { + return cbi(null, callback); + } + + @Override + public FutureDependencyBuilder<F> cbi(Object callbackInstance, String callback) { + super.setCallbacks(callbackInstance, callback, null); + return this; + } + + @Override + public <T> FutureDependencyBuilder<F> cb(CbTypeFuture<T, ? super F> consumer) { + return cb(consumer, false); + } + + @SuppressWarnings("unchecked") + @Override + public <T> FutureDependencyBuilder<F> cb(CbTypeFuture<T, ? super F> consumer, boolean async) { + m_accept2Type = Helpers.getLambdaArgType(consumer, 0);; + m_accept2 = (instance, result) -> consumer.accept((T) instance, result); + m_async = async; + return this; + } + + @Override + public <T> FutureDependencyBuilder<F> cb(CbTypeFuture<T, ? super F> consumer, Executor executor) { + cb(consumer, true /* async */); + m_exec = executor; + return this; + } + + @Override + public FutureDependencyBuilder<F> cbi(CbFuture<? super F> consumer) { + cbi(consumer, false); + return this; + } + + @Override + public FutureDependencyBuilder<F> cbi(CbFuture<? super F> consumer, boolean async) { + m_accept = m_accept.andThen(future -> consumer.accept(future)); + m_async = async; + return this; + } + + @Override + public FutureDependencyBuilder<F> cbi(CbFuture<? super F> consumer, Executor executor) { + cbi(consumer, true /* async */); + m_exec = executor; + return this; + } + + // ---------- DependencyContext interface ---------- + + @Override + public void start() { + try { + if (m_async) { + if (m_exec != null) { + m_future.whenCompleteAsync((result, error) -> completed(result, error), m_exec); + } else { + m_future.whenCompleteAsync((result, error) -> completed(result, error)); + } + } else { + m_future.whenComplete((result, error) -> completed(result, error)); + } + } catch (Throwable error) { + super.getComponentContext().getLogger().log(LogService.LOG_ERROR, "completable future failed", error); + } + super.start(); + } + + @Override + public DependencyContext createCopy() { + return new CompletableFutureDependencyImpl<F>(m_comp, this); + } + + @Override + public Class<?> getAutoConfigType() { + return null; // we don't support auto config mode + } + + // ---------- ComponentDependencyDeclaration interface ----------- + + /** + * Returns the name of this dependency (a generic name with optional info + * separated by spaces). The DM Shell will use this method when displaying + * the dependency + **/ + @Override + public String getSimpleName() { + return m_future.toString(); + } + + /** + * Returns the name of the type of this dependency. Used by the DM shell + * when displaying the dependency. + **/ + @Override + public String getType() { + return "future"; + } + + /** + * Called by DM component implementation when all required dependencies are satisfied. + */ + @Override + public void invokeCallback(EventType type, Event ... events) { + try { + switch (type) { + case ADDED: + if (m_add != null) { + // Inject result by reflection on a method name + injectByReflection(events[0].getEvent()); + return; + } + F result = events[0].getEvent(); + if (m_accept2 != null) { + if (m_accept2Type != null) { + // find the component instance that matches the given type + Object componentInstance = Stream.of(getComponentContext().getInstances()) + .filter(instance -> Helpers.getClass(instance).equals(m_accept2Type)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + "accept callback is not on one of the component instances: " + m_accept2 + " (type=" + m_accept2Type + ")")); + + m_accept2.accept(componentInstance, result); + } else { + // invoke a method in the main component instance that will handle the completed future. + m_accept2.accept(getComponentContext().getInstance(), result); + } + } else { + // Just invoke the Consumer with the completed future + m_accept.accept(result); + } + break; + + default: + break; + } + } catch (Throwable exc) { + super.getComponentContext().getLogger().log(LogService.LOG_ERROR, "completable future failed", exc); + } + } + + // ---------- Private methods ----------- + + /** + * Triggers component activation when the future has completed. + * @param result + * @param error + */ + private void completed(F result, Throwable error) { + if (error != null) { + super.getComponentContext().getLogger().log(LogService.LOG_ERROR, "completable future failed", error); + } else { + // Will trigger component activation (if other dependencies are satisfied), and our invokeCallback method will then be called. + m_component.handleEvent(this, EventType.ADDED, new Event(result)); + } + } + + /** + * Injects the completed fiture result in a method by reflection. + * We try to find a method which has in its signature a parameter that is compatible with the future result + * (including any interfaces the result may implements). + * + * @param result the result of the completable future. + */ + private void injectByReflection(Object result) { + List<Class<?>> types = new ArrayList<>(); + Class<?> currentClazz = result.getClass(); + + while (currentClazz != null && currentClazz != Object.class) { + types.add(currentClazz); + Stream.of(currentClazz.getInterfaces()).forEach(types::add); + currentClazz = currentClazz.getSuperclass(); + } + + Class<?>[][] classes = new Class<?>[types.size() + 1][1]; + Object[][] results = new Object[types.size() + 1][1]; + for (int i = 0; i < types.size(); i ++) { + classes[i] = new Class<?>[] { types.get(i) }; + results[i] = new Object[] { result }; + } + classes[types.size()] = new Class<?>[0]; + results[types.size()] = new Object[0]; + m_component.invokeCallbackMethod(getInstances(), m_add, classes, results); + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ComponentBuilderImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ComponentBuilderImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ComponentBuilderImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ComponentBuilderImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,728 @@ +package org.apache.felix.dm.lambda.impl; + +import static org.apache.felix.dm.lambda.impl.ComponentBuilderImpl.ComponentCallback.DESTROY; +import static org.apache.felix.dm.lambda.impl.ComponentBuilderImpl.ComponentCallback.INIT; +import static org.apache.felix.dm.lambda.impl.ComponentBuilderImpl.ComponentCallback.START; +import static org.apache.felix.dm.lambda.impl.ComponentBuilderImpl.ComponentCallback.STOP; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.Dependency; +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.dm.lambda.BundleDependencyBuilder; +import org.apache.felix.dm.lambda.ComponentBuilder; +import org.apache.felix.dm.lambda.ConfigurationDependencyBuilder; +import org.apache.felix.dm.lambda.DependencyBuilder; +import org.apache.felix.dm.lambda.FluentProperties; +import org.apache.felix.dm.lambda.FutureDependencyBuilder; +import org.apache.felix.dm.lambda.ServiceDependencyBuilder; +import org.apache.felix.dm.lambda.callbacks.CbComponent; +import org.apache.felix.dm.lambda.callbacks.CbConsumer; +import org.apache.felix.dm.lambda.callbacks.CbTypeComponent; + +public class ComponentBuilderImpl implements ComponentBuilder<ComponentBuilderImpl> { + private final List<DependencyBuilder<?>> m_dependencyBuilders = new ArrayList<>(); + private final Component m_component; + private final boolean m_componentUpdated; + private String[] m_serviceNames; + private Dictionary<Object, Object> m_properties; + private Object m_impl; + private Object m_factory; + private boolean m_factoryHasComposite; + private boolean m_autoAdd = true; + protected final Map<ComponentCallback, List<MethodRef<Object>>> m_refs = new HashMap<>(); + private Object m_compositionInstance; + private String m_compositionMethod; + private String m_init; + private String m_stop; + private String m_start; + private String m_destroy; + private Object m_callbackInstance; + private String m_factoryCreateMethod; + private boolean m_hasFactoryRef; + private boolean m_hasFactory; + + enum ComponentCallback { INIT, START, STOP, DESTROY }; + + @FunctionalInterface + interface MethodRef<I> { + public void accept(I instance, Component c); + } + + public ComponentBuilderImpl(DependencyManager dm) { + m_component = dm.createComponent(); + m_componentUpdated = false; + } + + public ComponentBuilderImpl(Component component, boolean update) { + m_component = component; + m_componentUpdated = update; + } + + @Override + public ComponentBuilderImpl autoConfig(Class<?> clazz, boolean autoConfig) { + m_component.setAutoConfig(clazz, autoConfig); + return this; + } + + @Override + public ComponentBuilderImpl autoConfig(Class<?> clazz, String instanceName) { + m_component.setAutoConfig(clazz, instanceName); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?> iface) { + m_serviceNames = new String[] {iface.getName()}; + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?> iface, String name, Object value, Object ... rest) { + provides(iface); + properties(name, value, rest); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?> iface, FluentProperties ... properties) { + provides(iface); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?> iface, Dictionary<?,?> properties) { + provides(iface); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?>[] ifaces) { + m_serviceNames = Stream.of(ifaces).map(c -> c.getName()).toArray(String[]::new); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?>[] ifaces, String name, Object value, Object ... rest) { + provides(ifaces); + properties(name, value, rest); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?>[] ifaces, FluentProperties ... properties) { + provides(ifaces); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(Class<?>[] ifaces, Dictionary<?,?> properties) { + provides(ifaces); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(String iface) { + m_serviceNames = new String[] {iface}; + return this; + } + + @Override + public ComponentBuilderImpl provides(String iface, String name, Object value, Object ... rest) { + provides(iface); + properties(name, value, rest); + return this; + } + + @Override + public ComponentBuilderImpl provides(String iface, FluentProperties ... properties) { + provides(iface); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(String iface, Dictionary<?,?> properties) { + provides(iface); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(String[] ifaces) { + m_serviceNames = ifaces; + return this; + } + + @Override + public ComponentBuilderImpl provides(String[] ifaces, String name, Object value, Object ... rest) { + provides(ifaces); + properties(name, value, rest); + return this; + } + + @Override + public ComponentBuilderImpl provides(String[] ifaces, FluentProperties ... properties) { + provides(ifaces); + properties(properties); + return this; + } + + @Override + public ComponentBuilderImpl provides(String[] ifaces, Dictionary<?,?> properties) { + provides(ifaces); + properties(properties); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public ComponentBuilderImpl properties(Dictionary<?, ?> properties) { + m_properties = (Dictionary<Object, Object>) properties; + return this; + } + + @Override + public ComponentBuilderImpl properties(String name, Object value, Object ... rest) { + Objects.nonNull(name); + Objects.nonNull(value); + Properties props = new Properties(); + props.put(name, value); + if ((rest.length & 1) != 0) { + throw new IllegalArgumentException("Invalid number of specified properties (number of arguments must be even)."); + } + for (int i = 0; i < rest.length - 1; i += 2) { + String k = rest[i].toString().trim(); + Object v = rest[i+1]; + props.put(k, v); + } + m_properties = props; + return this; + } + + @Override + public ComponentBuilderImpl properties(FluentProperties ... properties) { + Dictionary<Object, Object> props = new Hashtable<>(); + Stream.of(properties).forEach(property -> { + String name = Helpers.getLambdaParameterName(property, 0); + if (name.equals("arg0")) { + throw new IllegalArgumentException("arg0 property name not supported"); + } + Object value = property.apply(name); + props.put(name, value); + }); + m_properties = props; + return this; + } + + @Override + public ComponentBuilderImpl debug(String label) { + m_component.setDebug(label); + return this; + } + + @Override + public ComponentBuilderImpl autoAdd(boolean autoAdd) { + m_autoAdd = autoAdd; + return this; + } + + public ComponentBuilderImpl autoAdd() { + m_autoAdd = true; + return this; + } + + public boolean isAutoAdd() { + return m_autoAdd; + } + + @Override + public ComponentBuilderImpl impl(Object instance) { + m_impl = instance; + return this; + } + + @Override + public ComponentBuilderImpl factory(Object factory, String createMethod) { + m_factory = factory; + m_factoryCreateMethod = createMethod; + ensureHasNoFactoryRef(); + m_hasFactory = true; + return this; + } + + @Override + public ComponentBuilderImpl factory(Supplier<?> create) { + Objects.nonNull(create); + ensureHasNoFactory(); + m_hasFactoryRef = true; + m_factory = new Object() { + @SuppressWarnings("unused") + public Object create() { + return create.get(); + } + }; + return this; + } + + @Override + public <U, V> ComponentBuilderImpl factory(Supplier<U> supplier, Function<U, V> create) { + Objects.nonNull(supplier); + Objects.nonNull(create); + ensureHasNoFactory(); + m_hasFactoryRef = true; + + m_factory = new Object() { + @SuppressWarnings("unused") + public Object create() { + U factoryImpl = supplier.get(); + return create.apply(factoryImpl); + } + }; + return this; + } + + @Override + public ComponentBuilderImpl factory(Supplier<?> create, Supplier<Object[]> getComposite) { + Objects.nonNull(create); + Objects.nonNull(getComposite); + ensureHasNoFactory(); + m_hasFactoryRef = true; + + m_factory = new Object() { + @SuppressWarnings("unused") + public Object create() { // Create Factory instance + return create.get(); + } + + @SuppressWarnings("unused") + public Object[] getComposite() { // Create Factory instance + return getComposite.get(); + } + }; + m_factoryHasComposite = true; + return this; + } + + @Override + public <U> ComponentBuilderImpl factory(Supplier<U> factorySupplier, Function<U, ?> factoryCreate, Function<U, Object[]> factoryGetComposite) { + Objects.nonNull(factorySupplier); + Objects.nonNull(factoryCreate); + Objects.nonNull(factoryGetComposite); + ensureHasNoFactory(); + m_hasFactoryRef = true; + + m_factory = new Object() { + U m_factoryInstance; + + @SuppressWarnings("unused") + public Object create() { + m_factoryInstance = factorySupplier.get(); + return factoryCreate.apply(m_factoryInstance); + } + + @SuppressWarnings("unused") + public Object[] getComposite() { + return factoryGetComposite.apply(m_factoryInstance); + } + }; + m_factoryHasComposite = true; + return this; + } + + public ComponentBuilderImpl composition(String getCompositionMethod) { + return composition(null, getCompositionMethod); + } + + public ComponentBuilderImpl composition(Object instance, String getCompositionMethod) { + m_compositionInstance = instance; + m_compositionMethod = getCompositionMethod; + return this; + } + + public ComponentBuilderImpl composition(Supplier<Object[]> getCompositionMethod) { + m_compositionInstance = new Object() { + @SuppressWarnings("unused") + public Object[] getComposition() { + return getCompositionMethod.get(); + } + }; + m_compositionMethod = "getComposition"; + return this; + } + + @Override + public ComponentBuilderImpl withSrv(Class<?> service, String filter) { + return withSrv(service, srv->srv.filter(filter)); + } + + @Override + public ComponentBuilderImpl withSrv(Class<?> ... services) { + for (Class<?> s : services) { + doWithService(s); + } + return this; + } + + private <U> void doWithService(Class<U> service) { + ServiceDependencyBuilder<U> dep = new ServiceDependencyBuilderImpl<>(m_component, service); + m_dependencyBuilders.add(dep); + } + + @Override + public <U> ComponentBuilderImpl withSrv(Class<U> service, Consumer<ServiceDependencyBuilder<U>> consumer) { + ServiceDependencyBuilder<U> dep = new ServiceDependencyBuilderImpl<>(m_component, service); + consumer.accept(dep); + m_dependencyBuilders.add(dep); + return this; + } + + @Override + public ComponentBuilderImpl withCnf(Consumer<ConfigurationDependencyBuilder> consumer) { + ConfigurationDependencyBuilder dep = new ConfigurationDependencyBuilderImpl(m_component); + consumer.accept(dep); + m_dependencyBuilders.add(dep); + return this; + } + + @Override + public ComponentBuilderImpl withBundle(Consumer<BundleDependencyBuilder> consumer) { + BundleDependencyBuilder dep = new BundleDependencyBuilderImpl(m_component); + consumer.accept(dep); + m_dependencyBuilders.add(dep); + return this; + } + + @Override + public <V> ComponentBuilderImpl withFuture(CompletableFuture<V> future, Consumer<FutureDependencyBuilder<V>> consumer) { + FutureDependencyBuilder<V> dep = new CompletableFutureDependencyImpl<>(m_component, future); + consumer.accept(dep); + m_dependencyBuilders.add(dep); + return this; + } + + public ComponentBuilderImpl init(String callback) { + ensureHasNoLifecycleMethodRefs(); + m_init = callback; + return this; + } + + public ComponentBuilderImpl start(String callback) { + ensureHasNoLifecycleMethodRefs(); + m_start = callback; + return this; + } + + public ComponentBuilderImpl stop(String callback) { + ensureHasNoLifecycleMethodRefs(); + m_stop = callback; + return this; + } + + public ComponentBuilderImpl destroy(String callback) { + ensureHasNoLifecycleMethodRefs(); + m_destroy = callback; + return this; + } + + public ComponentBuilderImpl init(Object callbackInstance, String callback) { + m_callbackInstance = callbackInstance; + return init(callback); + } + + public ComponentBuilderImpl start(Object callbackInstance, String callback) { + m_callbackInstance = callbackInstance; + return start(callback); + } + + public ComponentBuilderImpl stop(Object callbackInstance, String callback) { + m_callbackInstance = callbackInstance; + return stop(callback); + } + + public ComponentBuilderImpl destroy(Object callbackInstance, String callback) { + m_callbackInstance = callbackInstance; + return destroy(callback); + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl init(CbConsumer<U> callback) { + if (callback != null) { + setComponentCallbackRef(INIT, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl start(CbConsumer<U> callback) { + if (callback != null) { + setComponentCallbackRef(START, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl stop(CbConsumer<U> callback) { + if (callback != null) { + setComponentCallbackRef(STOP, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl destroy(CbConsumer<U> callback) { + if (callback != null) { + setComponentCallbackRef(DESTROY, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl init(CbTypeComponent<U> callback) { + if (callback != null) { + setComponentCallbackRef(INIT, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst, component)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl start(CbTypeComponent<U> callback) { + if (callback != null) { + setComponentCallbackRef(START, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst, component)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl stop(CbTypeComponent<U> callback) { + if (callback != null) { + setComponentCallbackRef(STOP, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst, component)); + } + return this; + } + + @SuppressWarnings("unchecked") + @Override + public <U> ComponentBuilderImpl destroy(CbTypeComponent<U> callback) { + if (callback != null) { + setComponentCallbackRef(DESTROY, Helpers.getLambdaArgType(callback, 0), (inst, component) -> callback.accept((U) inst, component)); + } + return this; + } + + @Override + public ComponentBuilderImpl initInstance(Runnable callback) { + if (callback != null) { + setInstanceCallbackRef(INIT, (inst, component) -> callback.run()); + } + return this; + } + + @Override + public ComponentBuilderImpl startInstance(Runnable callback) { + if (callback != null) { + setInstanceCallbackRef(START, (inst, component) -> callback.run()); + } + return this; + } + + @Override + public ComponentBuilderImpl stopInstance(Runnable callback) { + if (callback != null) { + setInstanceCallbackRef(STOP, (inst, component) -> callback.run()); + } + return this; + } + + @Override + public ComponentBuilderImpl destroyInstance(Runnable callback) { + if (callback != null) { + setInstanceCallbackRef(DESTROY, (inst, component) -> callback.run()); + } + return this; + } + + @Override + public ComponentBuilderImpl initInstance(CbComponent callback) { + if (callback != null) { + setInstanceCallbackRef(INIT, (inst, component) -> callback.accept(component)); + } + return this; + } + + @Override + public ComponentBuilderImpl startInstance(CbComponent callback) { + if (callback != null) { + setInstanceCallbackRef(START, (inst, component) -> callback.accept(component)); + } + return this; + } + + @Override + public ComponentBuilderImpl stopInstance(CbComponent callback) { + if (callback != null) { + setInstanceCallbackRef(STOP, (inst, component) -> callback.accept(component)); + } + return this; + } + + @Override + public ComponentBuilderImpl destroyInstance(CbComponent callback) { + if (callback != null) { + setInstanceCallbackRef(DESTROY, (inst, component) -> callback.accept(component)); + } + return this; + } + + public Component build() { + if (m_serviceNames != null) { + m_component.setInterface(m_serviceNames, m_properties); + } + + if (m_properties != null) { + m_component.setServiceProperties(m_properties); + } + + if (! m_componentUpdated) { // Don't override impl or set callbacks if component is being updated + if (m_impl != null) { + m_component.setImplementation(m_impl); + m_component.setComposition(m_compositionInstance, m_compositionMethod); + } else { + Objects.nonNull(m_factory); + if (m_hasFactoryRef) { + m_component.setFactory(m_factory, "create"); + if (m_factoryHasComposite) { + m_component.setComposition(m_factory, "getComposite"); + } + } else { + m_component.setFactory(m_factory, m_factoryCreateMethod); + } + } + + if (m_refs.size() > 0) { + setLifecycleMethodRefs(); + } else if (hasLifecleMethods()) { + m_component.setCallbacks(m_callbackInstance, m_init, m_start, m_stop, m_destroy); + } + } + + if (m_dependencyBuilders.size() > 0) { + // add atomically in case we are building some component dependencies from a component init method. + // We first transform the list of builders into a stream of built Dependencies, then we collect the result + // to an array of Dependency[]. + m_component.add(m_dependencyBuilders.stream().map(builder -> builder.build()).toArray(Dependency[]::new)); + } + return m_component; + } + + private boolean hasLifecleMethods() { + return m_init != null || m_start != null || m_stop != null || m_destroy != null; + } + + private boolean hasLifecleMethodRefs() { + return m_refs.size() > 0; + } + + private void ensureHasNoLifecycleMethods() { + if (hasLifecleMethods()) { + throw new IllegalStateException("Can't mix method references and name names for lifecycle callbacks"); + } + } + + private void ensureHasNoLifecycleMethodRefs() { + if (hasLifecleMethodRefs()) { + throw new IllegalStateException("Can't mix method references and name names for lifecycle callbacks"); + } + } + + protected <U> ComponentBuilderImpl setInstanceCallbackRef(ComponentCallback cbType, MethodRef<U> ref) { + ensureHasNoLifecycleMethods(); + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component) -> { + ref.accept(null, component); + }); + return this; + } + + @SuppressWarnings("unchecked") + private <U> ComponentBuilderImpl setComponentCallbackRef(ComponentCallback cbType, Class<U> type, MethodRef<U> callback) { + ensureHasNoLifecycleMethods(); + if (type.equals(Object.class)) { + throw new IllegalStateException("callback does not seam to be one from the possible component implementation classes"); + } + List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>()); + list.add((instance, component) -> { + Object componentImpl = Stream.of(component.getInstances()) + .filter(impl -> Helpers.getClass(impl).equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("The method reference " + callback + " does not match any available component impl classes.")); + callback.accept((U) componentImpl, component); + }); + return this; + } + + @SuppressWarnings("unused") + private void setLifecycleMethodRefs() { + Object cb = new Object() { + void init(Component comp) { + invokeLfcleCallbacks(ComponentCallback.INIT, comp); + } + + void start(Component comp) { + invokeLfcleCallbacks(ComponentCallback.START, comp); + } + + void stop(Component comp) { + invokeLfcleCallbacks(ComponentCallback.STOP, comp); + } + + void destroy(Component comp) { + invokeLfcleCallbacks(ComponentCallback.DESTROY, comp); + } + }; + m_component.setCallbacks(cb, "init", "start", "stop", "destroy"); + } + + private void invokeLfcleCallbacks(ComponentCallback cbType, Component component) { + m_refs.computeIfPresent(cbType, (k, mrefs) -> { + mrefs.forEach(mref -> mref.accept(null, component)); + return mrefs; + }); + } + + private void ensureHasNoFactoryRef() { + if (m_hasFactoryRef) { + throw new IllegalStateException("Can't mix factory method name and factory method reference"); + } + } + + private void ensureHasNoFactory() { + if (m_hasFactory) { + throw new IllegalStateException("Can't mix factory method name and factory method reference"); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ConfigurationDependencyBuilderImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ConfigurationDependencyBuilderImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ConfigurationDependencyBuilderImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/ConfigurationDependencyBuilderImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,162 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.ConfigurationDependency; +import org.apache.felix.dm.lambda.ConfigurationDependencyBuilder; +import org.apache.felix.dm.lambda.callbacks.CbComponentDictionary; +import org.apache.felix.dm.lambda.callbacks.CbDictionary; +import org.apache.felix.dm.lambda.callbacks.CbTypeComponentDictionary; +import org.apache.felix.dm.lambda.callbacks.CbTypeDictionary; + +public class ConfigurationDependencyBuilderImpl implements ConfigurationDependencyBuilder { + private String m_pid; + private boolean m_propagate; + private final Component m_component; + private String m_updateMethodName; + private Object m_updateCallbackInstance; + private boolean m_hasMethodRefs; + private boolean m_hasReflectionCallback; + private final List<MethodRef<Object>> m_refs = new ArrayList<>(); + private boolean m_hasComponentCallbackRefs; + private boolean m_needsInstance = false; + + @FunctionalInterface + interface MethodRef<I> { + public void accept(I instance, Component c, Dictionary<String, Object> props); + } + + public ConfigurationDependencyBuilderImpl(Component component) { + m_component = component; + } + + @Override + public ConfigurationDependencyBuilder pid(String pid) { + m_pid = pid; + return this; + } + + @Override + public ConfigurationDependencyBuilder pid(Class<?> pidClass) { + m_pid = pidClass.getName(); + return this; + } + + @Override + public ConfigurationDependencyBuilder propagate() { + m_propagate = true; + return this; + } + + @Override + public ConfigurationDependencyBuilder propagate(boolean propagate) { + m_propagate = propagate; + return this; + } + + public ConfigurationDependencyBuilder cb(String update) { + checkHasNoMethodRefs(); + m_hasReflectionCallback = true; + m_updateMethodName = update; + return this; + } + + public ConfigurationDependencyBuilder cbi(Object callbackInstance, String update) { + m_updateCallbackInstance = callbackInstance; + cb(update); + return this; + } + + public ConfigurationDependencyBuilder needsInstance(boolean needsInstance) { + m_needsInstance = needsInstance; + return this; + } + + @Override + public <T> ConfigurationDependencyBuilder cb(CbTypeDictionary<T> callback) { + Class<T> type = Helpers.getLambdaArgType(callback, 0); + return setComponentCallbackRef(type, (instance, component, props) -> { callback.accept((T) instance, props); }); + } + + @Override + public <T> ConfigurationDependencyBuilder cb(CbTypeComponentDictionary<T> callback) { + Class<T> type = Helpers.getLambdaArgType(callback, 0); + return setComponentCallbackRef(type, (instance, component, props) -> { callback.accept((T) instance, component, props); }); + } + + @Override + public ConfigurationDependencyBuilder cbi(CbDictionary callback) { + return setInstanceCallbackRef((instance, component, props) -> { callback.accept(props); }); + } + + @Override + public ConfigurationDependencyBuilder cbi(CbComponentDictionary callback) { + return setInstanceCallbackRef((instance, component, props) -> { callback.accept(component, props); }); + } + + @Override + public ConfigurationDependency build() { + ConfigurationDependency dep = m_component.getDependencyManager().createConfigurationDependency(); + Objects.nonNull(m_pid); + dep.setPid(m_pid); + dep.setPropagate(m_propagate); + if (m_updateMethodName != null) { + if (m_updateCallbackInstance != null) { + dep.setCallback(m_updateCallbackInstance, m_updateMethodName, m_needsInstance); + } else { + dep.setCallback(m_updateMethodName); + } + } else if (m_refs.size() > 0) { + // setup an internal callback object. When config is updated, we have to call each registered + // method references. + // Notice that we need the component to be instantiated in case there is a mref on one of the component instances (unbound method ref), or is used + // called "needsInstance(true)". + dep.setCallback(new Object() { + @SuppressWarnings("unused") + void updated(Component comp, Dictionary<String, Object> props) { + m_refs.forEach(mref -> mref.accept(null, comp, props)); + } + }, "updated", m_hasComponentCallbackRefs||m_needsInstance); + } + return dep; + } + + private <T> ConfigurationDependencyBuilder setInstanceCallbackRef(MethodRef<T> ref) { + checkHasNoReflectionCallbacks(); + m_hasMethodRefs = true; + m_refs.add((instance, component, props) -> ref.accept(null, component, props)); + return this; + } + + @SuppressWarnings("unchecked") + private <T> ConfigurationDependencyBuilder setComponentCallbackRef(Class<T> type, MethodRef<T> ref) { + checkHasNoReflectionCallbacks(); + m_hasMethodRefs = true; + m_hasComponentCallbackRefs = true; + m_refs.add((instance, component, props) -> { + Object componentImpl = Stream.of(component.getInstances()) + .filter(impl -> Helpers.getClass(impl).equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes.")); + ref.accept((T) componentImpl, component, props); + }); + return this; + } + + private void checkHasNoMethodRefs() { + if (m_hasMethodRefs) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } + + private void checkHasNoReflectionCallbacks() { + if (m_hasReflectionCallback) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/FactoryPidAdapterBuilderImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/FactoryPidAdapterBuilderImpl.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/FactoryPidAdapterBuilderImpl.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/FactoryPidAdapterBuilderImpl.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,166 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.dm.lambda.ComponentBuilder; +import org.apache.felix.dm.lambda.FactoryPidAdapterBuilder; +import org.apache.felix.dm.lambda.callbacks.CbComponentDictionary; +import org.apache.felix.dm.lambda.callbacks.CbDictionary; +import org.apache.felix.dm.lambda.callbacks.CbTypeComponentDictionary; +import org.apache.felix.dm.lambda.callbacks.CbTypeDictionary; + +public class FactoryPidAdapterBuilderImpl implements AdapterBase<FactoryPidAdapterBuilder>, FactoryPidAdapterBuilder { + private String m_factoryPid; + private boolean m_propagate; + private final DependencyManager m_dm; + private boolean m_autoAdd = true; + private String m_updateMethodName; + private Object m_updateCallbackInstance; + private boolean m_hasMethodRefs; + private boolean m_hasReflectionCallback; + private Consumer<ComponentBuilder<?>> m_compBuilder = (componentBuilder -> {}); + private final List<MethodRef<Object>> m_refs = new ArrayList<>(); + + @FunctionalInterface + interface MethodRef<I> { + public void accept(I instance, Component c, Dictionary<String, Object> props); + } + + public FactoryPidAdapterBuilderImpl(DependencyManager dm) { + m_dm = dm; + } + + public void andThenBuild(Consumer<ComponentBuilder<?>> builder) { + m_compBuilder = m_compBuilder.andThen(builder); + } + + @Override + public FactoryPidAdapterBuilderImpl autoAdd(boolean autoAdd) { + m_autoAdd = autoAdd; + return this; + } + + public boolean isAutoAdd() { + return m_autoAdd; + } + + @Override + public FactoryPidAdapterBuilder factoryPid(String pid) { + m_factoryPid = pid; + return this; + } + + @Override + public FactoryPidAdapterBuilder factoryPid(Class<?> pidClass) { + m_factoryPid = pidClass.getName(); + return this; + } + + @Override + public FactoryPidAdapterBuilder propagate() { + m_propagate = true; + return this; + } + + @Override + public FactoryPidAdapterBuilder propagate(boolean propagate) { + m_propagate = propagate; + return this; + } + + public FactoryPidAdapterBuilder cb(String update) { + checkHasNoMethodRefs(); + m_hasReflectionCallback = true; + m_updateMethodName = update; + return this; + } + + public FactoryPidAdapterBuilder cb(Object callbackInstance, String update) { + cb(update); + m_updateCallbackInstance = callbackInstance; + return this; + } + + @Override + public <U> FactoryPidAdapterBuilder cb(CbTypeDictionary<U> callback) { + Class<U> type = Helpers.getLambdaArgType(callback, 0); + return setComponentCallbackRef(type, (instance, component, props) -> { callback.accept((U) instance, props); }); + } + + @Override + public <U> FactoryPidAdapterBuilder cb(CbTypeComponentDictionary<U> callback) { + Class<U> type = Helpers.getLambdaArgType(callback, 0); + return setComponentCallbackRef(type, (instance, component, props) -> { callback.accept((U) instance, component, props); }); + } + + @Override + public FactoryPidAdapterBuilder cbi(CbDictionary callback) { + return setInstanceCallbackRef((instance, component, props) -> { callback.accept(props); }); + } + + @Override + public FactoryPidAdapterBuilder cbi(CbComponentDictionary callback) { + return setInstanceCallbackRef((instance, component, props) -> { callback.accept(component, props); }); + } + + @Override + public Component build() { + Objects.nonNull(m_factoryPid); + Component c = null; + + if (m_hasMethodRefs) { + Object wrapCallback = new Object() { + @SuppressWarnings("unused") + public void updated(Component comp, Dictionary<String, Object> conf) { + m_refs.forEach(mref -> mref.accept(null, comp, conf)); + } + }; + c = m_dm.createFactoryConfigurationAdapterService(m_factoryPid, "updated", m_propagate, wrapCallback); + } else { + c = m_dm.createFactoryConfigurationAdapterService(m_factoryPid, m_updateMethodName, m_propagate, m_updateCallbackInstance); + } + ComponentBuilderImpl cb = new ComponentBuilderImpl(c, false); + m_compBuilder.accept (cb); + return cb.build(); + } + + private <U> FactoryPidAdapterBuilder setInstanceCallbackRef(MethodRef<U> ref) { + checkHasNoReflectionCallbacks(); + m_hasMethodRefs = true; + m_refs.add((instance, component, props) -> ref.accept(null, component, props)); + return this; + } + + @SuppressWarnings("unchecked") + private <U> FactoryPidAdapterBuilder setComponentCallbackRef(Class<U> type, MethodRef<U> ref) { + checkHasNoReflectionCallbacks(); + m_hasMethodRefs = true; + m_refs.add((instance, component, props) -> { + Object componentImpl = Stream.of(component.getInstances()) + .filter(impl -> Helpers.getClass(impl).equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes.")); + ref.accept((U) componentImpl, component, props); + }); + return this; + } + + private void checkHasNoMethodRefs() { + if (m_hasMethodRefs) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } + + private void checkHasNoReflectionCallbacks() { + if (m_hasReflectionCallback) { + throw new IllegalStateException("Can't mix method references with reflection based callbacks"); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/Helpers.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/Helpers.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/Helpers.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/Helpers.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,147 @@ +package org.apache.felix.dm.lambda.impl; + +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.lambda.callbacks.SerializableLambda; + +/** + * Various helper methods related to generics and lambda expressions. + */ +public class Helpers { + private final static Pattern LAMBDA_INSTANCE_METHOD_TYPE = Pattern.compile("(L[^;]+)+"); + + /** + * Gets the class name of a given object. + * @param obj the object whose class has to be returned. + */ + public static Class<?> getClass(Object obj) { + Class<?> clazz = obj.getClass(); + if (Proxy.isProxyClass(clazz)) { + return Proxy.getProxyClass(clazz.getClassLoader(), clazz); + } + return clazz; + } + + /** + * Extracts the type of a given generic lambda parameter. + * Example: for "BiConsumer<String, Integer>", and with genericParamIndex=0, this method returns java.lang.String class. + * + * @param lambda a lambda expression, which must extends @link {@link SerializableLambda} interface. + * @param genericParamIndex the index of a given lambda generic parameter. + * @return the type of the lambda generic parameter that corresponds to the <code>genericParamIndex</code> + */ + @SuppressWarnings("unchecked") + public static <T> Class<T> getLambdaArgType(SerializableLambda lambda, int genericParamIndex) { + String[] lambdaParams = getGenericTypeStrings(lambda); + Class<?> clazz; + try { + clazz = lambda.getClass().getClassLoader().loadClass(lambdaParams[genericParamIndex]); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Can't load class " + lambdaParams[genericParamIndex]); + } + return (Class<T>) clazz; + } + + /** + * Extracts the first parameter of a lambda. + */ + public static String getLambdaParameterName(SerializableLambda lambda, int index) { + SerializedLambda serialized = getSerializedLambda(lambda); + Method m = getLambdaMethod(serialized, lambda.getClass().getClassLoader()); + Parameter p = m.getParameters()[index]; + + if (Objects.equals("arg0", p.getName())) { + throw new IllegalStateException("Can'f find lambda method name (Please check you are using javac -parameters option)."); + } + return p.getName(); + } + + /** + * Returns the SerializedObject of a given lambda. + */ + private static SerializedLambda getSerializedLambda(SerializableLambda lambda) { + if (lambda == null) { + throw new IllegalArgumentException(); + } + + for (Class<?> clazz = lambda.getClass(); clazz != null; clazz = clazz.getSuperclass()) { + try { + Method replaceMethod = clazz.getDeclaredMethod("writeReplace"); + replaceMethod.setAccessible(true); + Object serializedForm = replaceMethod.invoke(lambda); + + if (serializedForm instanceof SerializedLambda) { + return (SerializedLambda) serializedForm; + } + } + catch (NoSuchMethodException e) { + // fall through the loop and try the next class + } + catch (Throwable t) { + throw new RuntimeException("Error while extracting serialized lambda", t); + } + } + + throw new RuntimeException("writeReplace method not found"); + } + + /** + * Finds a composite + * @param component + * @param type + * @return + */ + @SuppressWarnings("unchecked") + public static <U> U findCompositeInstance(Component component, Class<U> type) { + U instance = (U) Stream.of(component.getInstances()) + .filter(inst -> Objects.equals(Helpers.getClass(inst), type)) + .findFirst() + .orElseThrow(() -> new RuntimeException("Did not find a component instance matching type " + type)); + return instance; + } + + /** + * Extracts the actual types of all lambda generic parameters. + * Example: for "BiConsumer<String, Integer>", this method returns ["java.lang.String", "java.lang.Integer"]. + */ + private static String[] getGenericTypeStrings(SerializableLambda lambda) { + // The only portable way to get the actual lambda generic parameters can be done using SerializedLambda. + SerializedLambda sl = getSerializedLambda(lambda); + String lambdaMethodType = sl.getInstantiatedMethodType(); + Matcher m = LAMBDA_INSTANCE_METHOD_TYPE.matcher(lambdaMethodType); + List<String> results = new ArrayList<>(); + while (m.find()) { + results.add(m.group().substring(1).replace("/", ".")); + } + return results.toArray(new String[0]); + } + + /** + * Extracts the actual java method from a given lambda. + */ + private static Method getLambdaMethod(SerializedLambda lambda, ClassLoader loader) { + String implClassName = lambda.getImplClass().replace('/', '.'); + Class<?> implClass; + try { + implClass = loader.loadClass(implClassName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Lambda Method not found (can not instantiate class " + implClassName); + } + + return Stream.of(implClass.getDeclaredMethods()) + .filter(method -> Objects.equals(method.getName(), lambda.getImplMethodName())) + .findFirst() + .orElseThrow(() -> new RuntimeException("Lambda Method not found")); + } + +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsDictionary.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsDictionary.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsDictionary.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsDictionary.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,94 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.NoSuchElementException; + +import org.osgi.framework.ServiceReference; + +/** + * Maps a ServiceReference to a Dictionary. + */ +public class SRefAsDictionary extends Dictionary<String, Object> { + private final ServiceReference<?> m_ref; + private volatile int m_size = -1; + + public SRefAsDictionary(ServiceReference<?> ref) { + m_ref = ref; + } + + @Override + public Object get(Object key) { + return m_ref.getProperty(key.toString()); + } + + @Override + public int size() { + return m_size != -1 ? m_size : (m_size = m_ref.getPropertyKeys().length); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public Enumeration<String> keys() { + return Collections.enumeration(Arrays.asList(m_ref.getPropertyKeys())); + } + + @Override + public Enumeration<Object> elements() { + final String[] keys = m_ref.getPropertyKeys(); + + return new Enumeration<Object>() { + int m_index = 0; + + @Override + public boolean hasMoreElements() { + return m_index < keys.length; + } + + @Override + public Object nextElement() { + if (m_index >= keys.length) { + throw new NoSuchElementException(); + } + return m_ref.getProperty(keys[m_index ++]); + } + }; + } + + @Override + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Object put(String key, Object value) { + throw new UnsupportedOperationException(); + } + + public String toString() { + int max = size() - 1; + if (max == -1) + return "{}"; + + StringBuilder sb = new StringBuilder(); + String[] keys = m_ref.getPropertyKeys(); + sb.append('{'); + for (int i = 0; ; i++) { + String key = keys[i]; + Object value = m_ref.getProperty(key); + sb.append(key); + sb.append('='); + sb.append(value == this ? "(this Dictionary)" : value.toString()); + + if (i == max) + return sb.append('}').toString(); + sb.append(", "); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsMap.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsMap.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsMap.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/impl/SRefAsMap.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,84 @@ +package org.apache.felix.dm.lambda.impl; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.osgi.framework.ServiceReference; + +/** + * Maps a ServiceReference to a Map. + */ +public class SRefAsMap extends AbstractMap<String, Object> { + private final ServiceReference<?> m_ref; + + public SRefAsMap(ServiceReference<?> ref) { + m_ref = ref; + } + + public Object get(Object key) { + return m_ref.getProperty(key.toString()); + } + + @Override + public Set<Entry<String, Object>> entrySet() { + return new AbstractSet<Entry<String, Object>>() { + @Override + public Iterator<Entry<String, Object>> iterator() { + final Enumeration<String> e = Collections.enumeration(Arrays.asList(m_ref.getPropertyKeys())); + + return new Iterator<Entry<String, Object>>() { + private String key; + + public boolean hasNext() { + return e.hasMoreElements(); + } + + public Entry<String, Object> next() { + key = e.nextElement(); + return new KeyEntry(key); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int size() { + return m_ref.getPropertyKeys().length; + } + }; + } + + @Override + public Object put(String key, Object value) { + throw new UnsupportedOperationException(); + } + + class KeyEntry implements Map.Entry<String, Object> { + private final String key; + + KeyEntry(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public Object getValue() { + return m_ref.getProperty(key); + } + + public Object setValue(Object value) { + return SRefAsMap.this.put(key, value); + } + } +}
