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