Pierre De Rop | 6e8f921 | 2016-02-20 21:44:59 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 19 | package org.apache.felix.dm.lambda.impl; |
| 20 | |
| 21 | import java.util.ArrayList; |
| 22 | import java.util.Dictionary; |
| 23 | import java.util.List; |
| 24 | import java.util.Objects; |
| 25 | import java.util.stream.Stream; |
| 26 | |
| 27 | import org.apache.felix.dm.Component; |
| 28 | import org.apache.felix.dm.ConfigurationDependency; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 29 | import org.apache.felix.dm.context.ComponentContext; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 30 | import org.apache.felix.dm.lambda.ConfigurationDependencyBuilder; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 31 | import org.apache.felix.dm.lambda.callbacks.CbConfiguration; |
| 32 | import org.apache.felix.dm.lambda.callbacks.CbConfigurationComponent; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 33 | import org.apache.felix.dm.lambda.callbacks.CbDictionary; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 34 | import org.apache.felix.dm.lambda.callbacks.CbDictionaryComponent; |
| 35 | import org.apache.felix.dm.lambda.callbacks.InstanceCbConfiguration; |
| 36 | import org.apache.felix.dm.lambda.callbacks.InstanceCbConfigurationComponent; |
| 37 | import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionary; |
| 38 | import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionaryComponent; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 39 | |
| 40 | public class ConfigurationDependencyBuilderImpl implements ConfigurationDependencyBuilder { |
| 41 | private String m_pid; |
| 42 | private boolean m_propagate; |
| 43 | private final Component m_component; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 44 | private String m_updateMethodName = "updated"; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 45 | private Object m_updateCallbackInstance; |
| 46 | private boolean m_hasMethodRefs; |
| 47 | private boolean m_hasReflectionCallback; |
| 48 | private final List<MethodRef<Object>> m_refs = new ArrayList<>(); |
| 49 | private boolean m_hasComponentCallbackRefs; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 50 | private Class<?> m_configType; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 51 | |
| 52 | @FunctionalInterface |
| 53 | interface MethodRef<I> { |
| 54 | public void accept(I instance, Component c, Dictionary<String, Object> props); |
| 55 | } |
| 56 | |
| 57 | public ConfigurationDependencyBuilderImpl(Component component) { |
| 58 | m_component = component; |
| 59 | } |
| 60 | |
| 61 | @Override |
| 62 | public ConfigurationDependencyBuilder pid(String pid) { |
| 63 | m_pid = pid; |
| 64 | return this; |
| 65 | } |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 66 | |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 67 | @Override |
| 68 | public ConfigurationDependencyBuilder propagate() { |
| 69 | m_propagate = true; |
| 70 | return this; |
| 71 | } |
| 72 | |
| 73 | @Override |
| 74 | public ConfigurationDependencyBuilder propagate(boolean propagate) { |
| 75 | m_propagate = propagate; |
| 76 | return this; |
| 77 | } |
| 78 | |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 79 | public ConfigurationDependencyBuilder update(String update) { |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 80 | checkHasNoMethodRefs(); |
| 81 | m_hasReflectionCallback = true; |
| 82 | m_updateMethodName = update; |
| 83 | return this; |
| 84 | } |
| 85 | |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 86 | public ConfigurationDependencyBuilder update(Class<?> configType, String updateMethod) { |
| 87 | m_configType = configType; |
| 88 | return update(updateMethod); |
| 89 | } |
| 90 | |
| 91 | public ConfigurationDependencyBuilder update(Object callbackInstance, String update) { |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 92 | m_updateCallbackInstance = callbackInstance; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 93 | update(update); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 94 | return this; |
| 95 | } |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 96 | |
| 97 | public ConfigurationDependencyBuilder update(Class<?> configType, Object callbackInstance, String update) { |
| 98 | m_updateCallbackInstance = callbackInstance; |
| 99 | return update(callbackInstance, update); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | @Override |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 103 | public <T> ConfigurationDependencyBuilder update(CbDictionary<T> callback) { |
| 104 | Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| 105 | return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| 106 | callback.accept((T) instance, props); |
| 107 | }); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | @Override |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 111 | public <T> ConfigurationDependencyBuilder update(CbDictionaryComponent<T> callback) { |
| 112 | Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| 113 | return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| 114 | callback.accept((T) instance, props, component); |
| 115 | }); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | @Override |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 119 | public <T, U> ConfigurationDependencyBuilder update(Class<U> configClass, CbConfiguration<T, U> callback) { |
| 120 | Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| 121 | m_pid = m_pid == null ? configClass.getName() : m_pid; |
| 122 | return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| 123 | U configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| 124 | callback.accept((T) instance, configProxy); |
| 125 | }); |
| 126 | } |
| 127 | |
| 128 | @Override |
| 129 | public <T, U> ConfigurationDependencyBuilder update(Class<U> configClass, CbConfigurationComponent<T, U> callback) { |
| 130 | Class<T> componentType = Helpers.getLambdaArgType(callback, 0); |
| 131 | m_pid = m_pid == null ? configClass.getName() : m_pid; |
| 132 | return setComponentCallbackRef(componentType, (instance, component, props) -> { |
| 133 | U configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| 134 | callback.accept((T) instance, configProxy, component); |
| 135 | }); |
| 136 | } |
| 137 | |
| 138 | @Override |
| 139 | public ConfigurationDependencyBuilder update(InstanceCbDictionary callback) { |
| 140 | return setInstanceCallbackRef((instance, component, props) -> { |
| 141 | callback.accept(props); |
| 142 | }); |
| 143 | } |
| 144 | |
| 145 | @Override |
| 146 | public ConfigurationDependencyBuilder update(InstanceCbDictionaryComponent callback) { |
| 147 | return setInstanceCallbackRef((instance, component, props) -> { |
| 148 | callback.accept(props, component); |
| 149 | }); |
| 150 | } |
| 151 | |
| 152 | public <T> ConfigurationDependencyBuilder update(Class<T> configClass, InstanceCbConfiguration<T> updated) { |
| 153 | m_pid = m_pid == null ? configClass.getName() : m_pid; |
| 154 | return setInstanceCallbackRef((instance, component, props) -> { |
| 155 | T configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| 156 | updated.accept(configProxy); |
| 157 | }); |
| 158 | } |
| 159 | |
| 160 | public <T> ConfigurationDependencyBuilder update(Class<T> configClass, InstanceCbConfigurationComponent<T> updated) { |
| 161 | m_pid = m_pid == null ? configClass.getName() : m_pid; |
| 162 | return setInstanceCallbackRef((instance, component, props) -> { |
| 163 | T configProxy = ((ComponentContext) m_component).createConfigurationProxy(configClass, props); |
| 164 | updated.accept(configProxy, component); |
| 165 | }); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | @Override |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 169 | public ConfigurationDependency build() { |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 170 | String pid = m_pid == null ? (m_configType != null ? m_configType.getName() : null) : m_pid; |
| 171 | if (pid == null) { |
| 172 | throw new IllegalStateException("Pid not specified"); |
| 173 | } |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 174 | ConfigurationDependency dep = m_component.getDependencyManager().createConfigurationDependency(); |
| 175 | Objects.nonNull(m_pid); |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 176 | dep.setPid(pid); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 177 | dep.setPropagate(m_propagate); |
| 178 | if (m_updateMethodName != null) { |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 179 | dep.setCallback(m_updateCallbackInstance, m_updateMethodName, m_configType); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 180 | } else if (m_refs.size() > 0) { |
| 181 | // setup an internal callback object. When config is updated, we have to call each registered |
| 182 | // method references. |
| 183 | // 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 |
| 184 | // called "needsInstance(true)". |
| 185 | dep.setCallback(new Object() { |
| 186 | @SuppressWarnings("unused") |
| 187 | void updated(Component comp, Dictionary<String, Object> props) { |
| 188 | m_refs.forEach(mref -> mref.accept(null, comp, props)); |
| 189 | } |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 190 | }, "updated", m_hasComponentCallbackRefs); |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 191 | } |
| 192 | return dep; |
| 193 | } |
| 194 | |
| 195 | private <T> ConfigurationDependencyBuilder setInstanceCallbackRef(MethodRef<T> ref) { |
| 196 | checkHasNoReflectionCallbacks(); |
| 197 | m_hasMethodRefs = true; |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 198 | m_updateMethodName = null; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 199 | m_refs.add((instance, component, props) -> ref.accept(null, component, props)); |
| 200 | return this; |
| 201 | } |
| 202 | |
| 203 | @SuppressWarnings("unchecked") |
| 204 | private <T> ConfigurationDependencyBuilder setComponentCallbackRef(Class<T> type, MethodRef<T> ref) { |
| 205 | checkHasNoReflectionCallbacks(); |
Pierre De Rop | 1152750 | 2016-02-18 21:07:16 +0000 | [diff] [blame] | 206 | m_updateMethodName = null; |
Pierre De Rop | faca289 | 2016-01-31 23:27:05 +0000 | [diff] [blame] | 207 | m_hasMethodRefs = true; |
| 208 | m_hasComponentCallbackRefs = true; |
| 209 | m_refs.add((instance, component, props) -> { |
| 210 | Object componentImpl = Stream.of(component.getInstances()) |
| 211 | .filter(impl -> Helpers.getClass(impl).equals(type)) |
| 212 | .findFirst() |
| 213 | .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes.")); |
| 214 | ref.accept((T) componentImpl, component, props); |
| 215 | }); |
| 216 | return this; |
| 217 | } |
| 218 | |
| 219 | private void checkHasNoMethodRefs() { |
| 220 | if (m_hasMethodRefs) { |
| 221 | throw new IllegalStateException("Can't mix method references with reflection based callbacks"); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | private void checkHasNoReflectionCallbacks() { |
| 226 | if (m_hasReflectionCallback) { |
| 227 | throw new IllegalStateException("Can't mix method references with reflection based callbacks"); |
| 228 | } |
| 229 | } |
| 230 | } |