blob: 2d8f2d9433710791a05366be48fcad852cf363a9 [file] [log] [blame]
Pierre De Ropfaca2892016-01-31 23:27:05 +00001package org.apache.felix.dm.lambda.impl;
2
3import java.util.ArrayList;
4import java.util.Dictionary;
5import java.util.List;
6import java.util.Objects;
7import java.util.stream.Stream;
8
9import org.apache.felix.dm.Component;
10import org.apache.felix.dm.ConfigurationDependency;
Pierre De Rop11527502016-02-18 21:07:16 +000011import org.apache.felix.dm.context.ComponentContext;
Pierre De Ropfaca2892016-01-31 23:27:05 +000012import org.apache.felix.dm.lambda.ConfigurationDependencyBuilder;
Pierre De Rop11527502016-02-18 21:07:16 +000013import org.apache.felix.dm.lambda.callbacks.CbConfiguration;
14import org.apache.felix.dm.lambda.callbacks.CbConfigurationComponent;
Pierre De Ropfaca2892016-01-31 23:27:05 +000015import org.apache.felix.dm.lambda.callbacks.CbDictionary;
Pierre De Rop11527502016-02-18 21:07:16 +000016import org.apache.felix.dm.lambda.callbacks.CbDictionaryComponent;
17import org.apache.felix.dm.lambda.callbacks.InstanceCbConfiguration;
18import org.apache.felix.dm.lambda.callbacks.InstanceCbConfigurationComponent;
19import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionary;
20import org.apache.felix.dm.lambda.callbacks.InstanceCbDictionaryComponent;
Pierre De Ropfaca2892016-01-31 23:27:05 +000021
22public class ConfigurationDependencyBuilderImpl implements ConfigurationDependencyBuilder {
23 private String m_pid;
24 private boolean m_propagate;
25 private final Component m_component;
Pierre De Rop11527502016-02-18 21:07:16 +000026 private String m_updateMethodName = "updated";
Pierre De Ropfaca2892016-01-31 23:27:05 +000027 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 Rop11527502016-02-18 21:07:16 +000032 private Class<?> m_configType;
Pierre De Ropfaca2892016-01-31 23:27:05 +000033
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 Rop11527502016-02-18 21:07:16 +000048
Pierre De Ropfaca2892016-01-31 23:27:05 +000049 @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 Rop11527502016-02-18 21:07:16 +000061 public ConfigurationDependencyBuilder update(String update) {
Pierre De Ropfaca2892016-01-31 23:27:05 +000062 checkHasNoMethodRefs();
63 m_hasReflectionCallback = true;
64 m_updateMethodName = update;
65 return this;
66 }
67
Pierre De Rop11527502016-02-18 21:07:16 +000068 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 Ropfaca2892016-01-31 23:27:05 +000074 m_updateCallbackInstance = callbackInstance;
Pierre De Rop11527502016-02-18 21:07:16 +000075 update(update);
Pierre De Ropfaca2892016-01-31 23:27:05 +000076 return this;
77 }
Pierre De Rop11527502016-02-18 21:07:16 +000078
79 public ConfigurationDependencyBuilder update(Class<?> configType, Object callbackInstance, String update) {
80 m_updateCallbackInstance = callbackInstance;
81 return update(callbackInstance, update);
Pierre De Ropfaca2892016-01-31 23:27:05 +000082 }
83
84 @Override
Pierre De Rop11527502016-02-18 21:07:16 +000085 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 Ropfaca2892016-01-31 23:27:05 +000090 }
91
92 @Override
Pierre De Rop11527502016-02-18 21:07:16 +000093 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 Ropfaca2892016-01-31 23:27:05 +000098 }
99
100 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000101 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 Ropfaca2892016-01-31 23:27:05 +0000148 }
149
150 @Override
Pierre De Ropfaca2892016-01-31 23:27:05 +0000151 public ConfigurationDependency build() {
Pierre De Rop11527502016-02-18 21:07:16 +0000152 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 Ropfaca2892016-01-31 23:27:05 +0000156 ConfigurationDependency dep = m_component.getDependencyManager().createConfigurationDependency();
157 Objects.nonNull(m_pid);
Pierre De Rop11527502016-02-18 21:07:16 +0000158 dep.setPid(pid);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000159 dep.setPropagate(m_propagate);
160 if (m_updateMethodName != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000161 dep.setCallback(m_updateCallbackInstance, m_updateMethodName, m_configType);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000162 } 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 Rop11527502016-02-18 21:07:16 +0000172 }, "updated", m_hasComponentCallbackRefs);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000173 }
174 return dep;
175 }
176
177 private <T> ConfigurationDependencyBuilder setInstanceCallbackRef(MethodRef<T> ref) {
178 checkHasNoReflectionCallbacks();
179 m_hasMethodRefs = true;
Pierre De Rop11527502016-02-18 21:07:16 +0000180 m_updateMethodName = null;
Pierre De Ropfaca2892016-01-31 23:27:05 +0000181 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 Rop11527502016-02-18 21:07:16 +0000188 m_updateMethodName = null;
Pierre De Ropfaca2892016-01-31 23:27:05 +0000189 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}