| /* |
| * 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 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.context.ComponentContext; |
| import org.apache.felix.dm.lambda.ConfigurationDependencyBuilder; |
| import org.apache.felix.dm.lambda.callbacks.CbConfiguration; |
| import org.apache.felix.dm.lambda.callbacks.CbConfigurationComponent; |
| import org.apache.felix.dm.lambda.callbacks.CbDictionary; |
| import org.apache.felix.dm.lambda.callbacks.CbDictionaryComponent; |
| import org.apache.felix.dm.lambda.callbacks.InstanceCbConfiguration; |
| import org.apache.felix.dm.lambda.callbacks.InstanceCbConfigurationComponent; |
| import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionary; |
| import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionaryComponent; |
| |
| public class ConfigurationDependencyBuilderImpl implements ConfigurationDependencyBuilder { |
| private String m_pid; |
| private boolean m_propagate; |
| private final Component m_component; |
| private String m_updateMethodName = "updated"; |
| 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 Class<?> m_configType; |
| |
| @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 propagate() { |
| m_propagate = true; |
| return this; |
| } |
| |
| @Override |
| public ConfigurationDependencyBuilder propagate(boolean propagate) { |
| m_propagate = propagate; |
| return this; |
| } |
| |
| public ConfigurationDependencyBuilder update(String update) { |
| checkHasNoMethodRefs(); |
| m_hasReflectionCallback = true; |
| m_updateMethodName = update; |
| return this; |
| } |
| |
| public ConfigurationDependencyBuilder update(Class<?> configType, String updateMethod) { |
| m_configType = configType; |
| return update(updateMethod); |
| } |
| |
| public ConfigurationDependencyBuilder update(Object callbackInstance, String update) { |
| m_updateCallbackInstance = callbackInstance; |
| update(update); |
| return this; |
| } |
| |
| public ConfigurationDependencyBuilder update(Class<?> configType, Object callbackInstance, String update) { |
| m_updateCallbackInstance = callbackInstance; |
| return update(callbackInstance, update); |
| } |
| |
| @Override |
| public <T> ConfigurationDependencyBuilder update(CbDictionary<T> callback) { |
| Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| callback.accept((T) instance, props); |
| }); |
| } |
| |
| @Override |
| public <T> ConfigurationDependencyBuilder update(CbDictionaryComponent<T> callback) { |
| Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| callback.accept((T) instance, props, component); |
| }); |
| } |
| |
| @Override |
| public <T, U> ConfigurationDependencyBuilder update(Class<U> configClass, CbConfiguration<T, U> callback) { |
| Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| m_pid = m_pid == null ? configClass.getName() : m_pid; |
| return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| U configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| callback.accept((T) instance, configProxy); |
| }); |
| } |
| |
| @Override |
| public <T, U> ConfigurationDependencyBuilder update(Class<U> configClass, CbConfigurationComponent<T, U> callback) { |
| Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| m_pid = m_pid == null ? configClass.getName() : m_pid; |
| return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| U configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| callback.accept((T) instance, configProxy, component); |
| }); |
| } |
| |
| @Override |
| public ConfigurationDependencyBuilder update(InstanceCbDictionary callback) { |
| return setInstanceCallbackRef((instance, component, props) -> { |
| callback.accept(props); |
| }); |
| } |
| |
| @Override |
| public ConfigurationDependencyBuilder update(InstanceCbDictionaryComponent callback) { |
| return setInstanceCallbackRef((instance, component, props) -> { |
| callback.accept(props, component); |
| }); |
| } |
| |
| public <T> ConfigurationDependencyBuilder update(Class<T> configClass, InstanceCbConfiguration<T> updated) { |
| m_pid = m_pid == null ? configClass.getName() : m_pid; |
| return setInstanceCallbackRef((instance, component, props) -> { |
| T configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| updated.accept(configProxy); |
| }); |
| } |
| |
| public <T> ConfigurationDependencyBuilder update(Class<T> configClass, InstanceCbConfigurationComponent<T> updated) { |
| m_pid = m_pid == null ? configClass.getName() : m_pid; |
| return setInstanceCallbackRef((instance, component, props) -> { |
| T configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| updated.accept(configProxy, component); |
| }); |
| } |
| |
| @Override |
| public ConfigurationDependency build() { |
| String pid = m_pid == null ? (m_configType != null ? m_configType.getName() : null) : m_pid; |
| if (pid == null) { |
| throw new IllegalStateException("Pid not specified"); |
| } |
| ConfigurationDependency dep = m_component.getDependencyManager().createConfigurationDependency(); |
| Objects.nonNull(m_pid); |
| dep.setPid(pid); |
| dep.setPropagate(m_propagate); |
| if (m_updateMethodName != null) { |
| dep.setCallback(m_updateCallbackInstance, m_updateMethodName, m_configType); |
| } 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); |
| } |
| return dep; |
| } |
| |
| private <T> ConfigurationDependencyBuilder setInstanceCallbackRef(MethodRef<T> ref) { |
| checkHasNoReflectionCallbacks(); |
| m_hasMethodRefs = true; |
| m_updateMethodName = null; |
| 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_updateMethodName = null; |
| 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"); |
| } |
| } |
| } |