blob: 6149a28be9076691db854e58653d668302926728 [file] [log] [blame]
/*
* 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.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.Component.ServiceScope;
import org.apache.felix.dm.ComponentStateListener;
import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.context.ComponentContext;
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.FluentProperty;
import org.apache.felix.dm.lambda.FutureDependencyBuilder;
import org.apache.felix.dm.lambda.ServiceDependencyBuilder;
import org.apache.felix.dm.lambda.callbacks.InstanceCb;
import org.apache.felix.dm.lambda.callbacks.InstanceCbComponent;
public class ComponentBuilderImpl implements ComponentBuilder<ComponentBuilderImpl> {
private final List<DependencyBuilder<?>> m_dependencyBuilders = new ArrayList<>();
private final List<Dependency> m_dependencies = 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, MethodRef> m_refs = new HashMap<>();
private Object m_compositionInstance;
private String m_compositionMethod;
private String m_init;
private String m_start;
private String m_stop;
private String m_destroy;
private String m_factoryCreateMethod;
private boolean m_hasFactoryRef;
private boolean m_hasFactory;
private Object m_initCallbackInstance;
private Object m_startCallbackInstance;
private Object m_stopCallbackInstance;
private Object m_destroyCallbackInstance;
private final List<ComponentStateListener> m_listeners = new ArrayList<>();
enum ComponentCallback { INIT, START, STOP, DESTROY };
@FunctionalInterface
interface MethodRef {
public void accept(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 scope(ServiceScope scope) {
m_component.setScope(scope);
return this;
}
@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, FluentProperty ... 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, FluentProperty ... 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, FluentProperty ... 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, FluentProperty ... 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(FluentProperty ... 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(convertDots(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();
}
@Override
public String toString() {
return create.getClass().getName() + " (Factory)";
}
};
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);
}
@Override
public String toString() {
return supplier.getClass().getName() + " (Factory)";
}
};
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();
}
@Override
public String toString() {
return create.getClass().getName() + " (Factory)";
}
};
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);
}
@Override
public String toString() {
return factorySupplier.getClass().getName() + " (Factory)";
}
};
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 withDep(Dependency dep) {
m_dependencies.add(dep);
return this;
}
@Override
public <U> ComponentBuilderImpl withSvc(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) {
m_init = callback;
return this;
}
public ComponentBuilderImpl init(Object callbackInstance, String callback) {
init(callback);
m_initCallbackInstance = callbackInstance;
return this;
}
@Override
public ComponentBuilderImpl init(InstanceCb callback) {
setCallbackMethodRef(INIT, component -> callback.callback());
m_init = null;
m_initCallbackInstance = null;
return this;
}
@Override
public ComponentBuilderImpl init(InstanceCbComponent callback) {
setCallbackMethodRef(INIT, component -> callback.accept(component));
m_init = null;
m_initCallbackInstance = null;
return this;
}
public ComponentBuilderImpl start(String callback) {
m_start = callback;
return this;
}
public ComponentBuilderImpl start(Object callbackInstance, String callback) {
start(callback);
m_startCallbackInstance = callbackInstance;
return this;
}
@Override
public ComponentBuilderImpl start(InstanceCb callback) {
setCallbackMethodRef(START, component -> callback.callback());
m_start = null;
m_startCallbackInstance = null;
return this;
}
@Override
public ComponentBuilderImpl start(InstanceCbComponent callback) {
setCallbackMethodRef(START, component -> callback.accept(component));
m_start = null;
m_startCallbackInstance = null;
return this;
}
public ComponentBuilderImpl stop(String callback) {
m_stop = callback;
return this;
}
public ComponentBuilderImpl stop(Object callbackInstance, String callback) {
stop(callback);
m_stopCallbackInstance = callbackInstance;
return this;
}
@Override
public ComponentBuilderImpl stop(InstanceCb callback) {
setCallbackMethodRef(STOP, component -> callback.callback());
m_stop = null;
m_stopCallbackInstance = null;
return this;
}
@Override
public ComponentBuilderImpl stop(InstanceCbComponent callback) {
setCallbackMethodRef(STOP, component -> callback.accept(component));
m_stop = null;
m_stopCallbackInstance = null;
return this;
}
public ComponentBuilderImpl destroy(String callback) {
m_destroy = callback;
return this;
}
public ComponentBuilderImpl destroy(Object callbackInstance, String callback) {
destroy(callback);
m_destroyCallbackInstance = callbackInstance;
return this;
}
@Override
public ComponentBuilderImpl destroy(InstanceCb callback) {
setCallbackMethodRef(DESTROY, component -> callback.callback());
m_destroy = null;
m_destroyCallbackInstance = null;
return this;
}
@Override
public ComponentBuilderImpl destroy(InstanceCbComponent callback) {
setCallbackMethodRef(DESTROY, component -> callback.accept(component));
m_destroy = null;
m_destroyCallbackInstance = null;
return this;
}
public ComponentBuilderImpl listener(ComponentStateListener listener) {
m_listeners.add(listener);
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);
}
m_listeners.stream().forEach(m_component::add);
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 (hasCallbacks()) { // either method refs on some object instances, or a callback (reflection) on some object instances.
if (m_refs.get(INIT) == null) {
setCallbackMethodRef(INIT, component -> invokeCallbacks(component, m_initCallbackInstance, m_init, "init"));
}
if (m_refs.get(START) == null) {
setCallbackMethodRef(START, component -> invokeCallbacks(component, m_startCallbackInstance, m_start, "start"));
}
if (m_refs.get(STOP) == null) {
setCallbackMethodRef(STOP, component -> invokeCallbacks(component, m_stopCallbackInstance, m_stop, "stop"));
}
if (m_refs.get(DESTROY) == null) {
setCallbackMethodRef(DESTROY, component -> invokeCallbacks(component, m_destroyCallbackInstance, m_destroy, "destroy"));
}
setInternalCallbacks();
}
}
if (m_dependencyBuilders.size() > 0) {
// add atomically in case we are building some component dependencies from a component init method.
List<Dependency> depList = new ArrayList<>();
m_dependencyBuilders.stream().map(builder -> builder.build()).forEach(depList::add);
depList.addAll(m_dependencies);
m_component.add(depList.stream().toArray(Dependency[]::new));
}
return m_component;
}
private boolean hasCallbacks() {
return m_refs.size() > 0 || m_init != null || m_start != null || m_stop != null || m_destroy != null;
}
private void invokeCallbacks(Component component, Object callbackInstance, String callback, String defaultCallback) {
boolean logIfNotFound = (callback != null);
callback = callback != null ? callback : defaultCallback;
ComponentContext ctx = (ComponentContext) component;
Object[] instances = callbackInstance != null ? new Object[] { callbackInstance } : ctx.getInstances();
ctx.invokeCallbackMethod(instances, callback,
new Class[][] {{ Component.class }, {}},
new Object[][] {{ component }, {}},
logIfNotFound);
}
private ComponentBuilderImpl setCallbackMethodRef(ComponentCallback cbType, MethodRef ref) {
m_refs.put(cbType, ref);
return this;
}
@SuppressWarnings("unused")
private void setInternalCallbacks() {
Object cb = new Object() {
void init(Component comp) {
invokeLifecycleCallback(ComponentCallback.INIT, comp);
}
void start(Component comp) {
invokeLifecycleCallback(ComponentCallback.START, comp);
}
void stop(Component comp) {
invokeLifecycleCallback(ComponentCallback.STOP, comp);
}
void destroy(Component comp) {
invokeLifecycleCallback(ComponentCallback.DESTROY, comp);
}
};
m_component.setCallbacks(cb, "init", "start", "stop", "destroy");
}
private void invokeLifecycleCallback(ComponentCallback cbType, Component component) {
m_refs.computeIfPresent(cbType, (k, mref) -> {
mref.accept(component);
return mref;
});
}
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");
}
}
private String convertDots(String propertyName) {
StringBuilder sb = new StringBuilder(propertyName);
// replace "__" by "_" or "_" by ".": foo_bar -> foo.bar; foo__BAR_zoo -> foo_BAR.zoo
for (int i = 0; i < sb.length(); i ++) {
if (sb.charAt(i) == '_') {
if (i < (sb.length() - 1) && sb.charAt(i+1) == '_') {
// replace foo__bar -> foo_bar
sb.replace(i, i+2, "_");
} else {
// replace foo_bar -> foo.bar
sb.replace(i, i+1, ".");
}
}
}
return sb.toString();
}
}