blob: e8004430a32b0d1a5a59ce928422d8593600cb15 [file] [log] [blame]
Pierre De Rop6e8f9212016-02-20 21:44:59 +00001/*
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 Ropfaca2892016-01-31 23:27:05 +000019package org.apache.felix.dm.lambda.impl;
20
21import java.util.ArrayList;
22import java.util.Dictionary;
23import java.util.HashMap;
24import java.util.List;
25import java.util.Map;
26import java.util.Objects;
Pierre De Rop11527502016-02-18 21:07:16 +000027import java.util.function.Function;
Pierre De Ropfaca2892016-01-31 23:27:05 +000028import java.util.stream.Stream;
29
30import org.apache.felix.dm.BundleDependency;
31import org.apache.felix.dm.Component;
32import org.apache.felix.dm.DependencyManager;
33import org.apache.felix.dm.lambda.BundleDependencyBuilder;
34import org.apache.felix.dm.lambda.callbacks.CbBundle;
Pierre De Rop11527502016-02-18 21:07:16 +000035import org.apache.felix.dm.lambda.callbacks.CbBundleComponent;
36import org.apache.felix.dm.lambda.callbacks.InstanceCbBundle;
37import org.apache.felix.dm.lambda.callbacks.InstanceCbBundleComponent;
Pierre De Ropfaca2892016-01-31 23:27:05 +000038import org.osgi.framework.Bundle;
39
40@SuppressWarnings("unchecked")
41public class BundleDependencyBuilderImpl implements BundleDependencyBuilder {
42 private String m_added;
43 private String m_changed;
44 private String m_removed;
45 private Object m_instance;
46 private boolean m_autoConfig = true;
47 private boolean m_autoConfigInvoked = false;
48 private boolean m_required = true;
49 private Bundle m_bundle;
50 private String m_filter;
51 private int m_stateMask = -1;
52 private boolean m_propagate;
53 private Object m_propagateInstance;
54 private String m_propagateMethod;
Pierre De Rop11527502016-02-18 21:07:16 +000055 private Function<Bundle, Dictionary<?, ?>> m_propagateCallback;
Pierre De Ropfaca2892016-01-31 23:27:05 +000056 private final Component m_component;
57
58 enum Cb {
59 ADD,
60 CHG,
61 REM
62 };
63
64 private final Map<Cb, List<MethodRef<Object>>> m_refs = new HashMap<>();
65
66 @FunctionalInterface
67 interface MethodRef<I> {
68 public void accept(I instance, Component c, Bundle bundle);
69 }
70
71 /**
72 * Class used to call a supplier that returns Propagated properties
73 */
74 private class Propagate {
75 @SuppressWarnings("unused")
Pierre De Rop11527502016-02-18 21:07:16 +000076 Dictionary<?, ?> propagate(Bundle bundle) {
77 return m_propagateCallback.apply(bundle);
Pierre De Ropfaca2892016-01-31 23:27:05 +000078 }
79 }
80
81 public BundleDependencyBuilderImpl (Component component) {
82 m_component = component;
83 }
84
85 @Override
86 public BundleDependencyBuilder autoConfig(boolean autoConfig) {
87 m_autoConfig = autoConfig;
88 m_autoConfigInvoked = true;
89 return this;
90 }
91
92 @Override
93 public BundleDependencyBuilder autoConfig() {
94 autoConfig(true);
95 return this;
96 }
97
98 @Override
99 public BundleDependencyBuilder required(boolean required) {
100 m_required = required;
101 return this;
102 }
103
104 @Override
105 public BundleDependencyBuilder required() {
106 required(true);
107 return this;
108 }
109
110 @Override
111 public BundleDependencyBuilder bundle(Bundle bundle) {
112 m_bundle = bundle;
113 return this;
114 }
115
116 @Override
117 public BundleDependencyBuilder filter(String filter) throws IllegalArgumentException {
118 m_filter = filter;
119 return this;
120 }
121
122 @Override
123 public BundleDependencyBuilder mask(int mask) {
124 m_stateMask = mask;
125 return this;
126 }
127
128 @Override
129 public BundleDependencyBuilder propagate(boolean propagate) {
130 m_propagate = propagate;
131 return this;
132 }
133
134 @Override
135 public BundleDependencyBuilder propagate() {
136 propagate(true);
137 return this;
138 }
139
140 @Override
141 public BundleDependencyBuilder propagate(Object instance, String method) {
Pierre De Rop11527502016-02-18 21:07:16 +0000142 if (m_propagateCallback != null || m_propagate) throw new IllegalStateException("Propagate callback already set.");
Pierre De Ropfaca2892016-01-31 23:27:05 +0000143 Objects.nonNull(method);
144 Objects.nonNull(instance);
145 m_propagateInstance = instance;
146 m_propagateMethod = method;
147 return this;
148 }
149
150 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000151 public BundleDependencyBuilder propagate(Function<Bundle, Dictionary<?, ?>> instance) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000152 if (m_propagateInstance != null || m_propagate) throw new IllegalStateException("Propagate callback already set.");
Pierre De Rop11527502016-02-18 21:07:16 +0000153 m_propagateCallback = instance;
Pierre De Ropfaca2892016-01-31 23:27:05 +0000154 return this;
155 }
Pierre De Ropfaca2892016-01-31 23:27:05 +0000156
157 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000158 public BundleDependencyBuilder callbackInstance(Object callbackInstance) {
159 m_instance = callbackInstance;
160 return this;
161 }
Pierre De Ropfaca2892016-01-31 23:27:05 +0000162
Pierre De Rop11527502016-02-18 21:07:16 +0000163 @Override
164 public BundleDependencyBuilder add(String add) {
165 callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000166 return this;
167 }
168
Pierre De Rop11527502016-02-18 21:07:16 +0000169 @Override
170 public BundleDependencyBuilder change(String change) {
171 callbacks(null, change, null);
172 return this;
173 }
174
175 @Override
176 public BundleDependencyBuilder remove(String remove) {
177 callbacks(null, null, remove);
178 return this;
179 }
180
181 private BundleDependencyBuilder callbacks(String added, String changed, String removed) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000182 requiresNoMethodRefs();
Pierre De Ropfaca2892016-01-31 23:27:05 +0000183 m_added = added != null ? added : m_added;
184 m_changed = changed != null ? changed : m_changed;
185 m_removed = removed != null ? removed : m_removed;
186 if (! m_autoConfigInvoked) m_autoConfig = false;
187 return this;
188 }
189
190 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000191 public <T> BundleDependencyBuilder add(CbBundle<T> add) {
192 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000193 }
194
195 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000196 public <T> BundleDependencyBuilder change(CbBundle<T> change) {
197 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000198 }
199
200 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000201 public <T> BundleDependencyBuilder remove(CbBundle<T> remove) {
202 return callbacks(null, null, remove);
203 }
204
205 private <T> BundleDependencyBuilder callbacks(CbBundle<T> add, CbBundle<T> change, CbBundle<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000206 if (add != null) {
207 setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, bundle));
208 }
209 if (change != null) {
210 setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, bundle));
211 }
212 if (remove != null) {
213 setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, bundle));
214 }
215 return this;
216 }
217
218 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000219 public <T> BundleDependencyBuilder add(CbBundleComponent<T> add) {
220 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000221 }
222
223 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000224 public <T> BundleDependencyBuilder change(CbBundleComponent<T> change) {
225 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000226 }
227
228 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000229 public <T> BundleDependencyBuilder remove(CbBundleComponent<T> remove) {
230 return callbacks(null, null, remove);
231 }
232
233 private <T> BundleDependencyBuilder callbacks(CbBundleComponent<T> add, CbBundleComponent<T> change, CbBundleComponent<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000234 if (add != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000235 setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000236 }
237 if (change != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000238 setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000239 }
240 if (remove != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000241 setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000242 }
243 return this;
244 }
245
246 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000247 public BundleDependencyBuilder add(InstanceCbBundle add) {
248 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000249 }
250
251 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000252 public BundleDependencyBuilder change(InstanceCbBundle change) {
253 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000254 }
255
256 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000257 public BundleDependencyBuilder remove(InstanceCbBundle remove) {
258 return callbacks(null, null, remove);
259 }
260
261 private BundleDependencyBuilder callbacks(InstanceCbBundle add, InstanceCbBundle change, InstanceCbBundle remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000262 if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(bundle));
263 if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(bundle));
264 if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(bundle));
265 return this;
266 }
267
268 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000269 public BundleDependencyBuilder add(InstanceCbBundleComponent add) {
270 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000271 }
272
273 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000274 public BundleDependencyBuilder change(InstanceCbBundleComponent add) {
275 return callbacks(add, null, null);
276 }
277
278 @Override
279 public BundleDependencyBuilder remove(InstanceCbBundleComponent remove) {
280 return callbacks(null, null, remove);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000281 }
282
Pierre De Rop11527502016-02-18 21:07:16 +0000283 private BundleDependencyBuilder callbacks(InstanceCbBundleComponent add, InstanceCbBundleComponent change, InstanceCbBundleComponent remove) {
284 if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(bundle, component));
285 if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(bundle, component));
286 if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000287 return this;
288 }
289
290 @Override
291 public BundleDependency build() {
292 DependencyManager dm = m_component.getDependencyManager();
293
294 BundleDependency dep = dm.createBundleDependency();
295 dep.setRequired(m_required);
296
297 if (m_filter != null) {
298 dep.setFilter(m_filter);
299 }
300
301 if (m_bundle != null) {
302 dep.setBundle(m_bundle);
303 }
304
305 if (m_stateMask != -1) {
306 dep.setStateMask(m_stateMask);
307 }
308
309 if (m_propagate) {
310 dep.setPropagate(true);
311 } else if (m_propagateInstance != null) {
312 dep.setPropagate(m_propagateInstance, m_propagateMethod);
Pierre De Rop11527502016-02-18 21:07:16 +0000313 } else if (m_propagateCallback != null) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000314 dep.setPropagate(new Propagate(), "propagate");
315 }
316
317 if (m_added != null || m_changed != null || m_removed != null) {
318 dep.setCallbacks(m_instance, m_added, m_changed, m_removed);
319 } else if (m_refs.size() > 0) {
320 Object cb = createCallbackInstance();
321 dep.setCallbacks(cb, "add", "change", "remove");
322 }
323
324 dep.setAutoConfig(m_autoConfig);
325 return dep;
326 }
327
328 private <T> BundleDependencyBuilder setInstanceCallbackRef(Cb cbType, MethodRef<T> ref) {
329 requiresNoStringCallbacks();
330 if (! m_autoConfigInvoked) m_autoConfig = false;
331 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
332 list.add((instance, component, bundle) -> ref.accept(null, component, bundle));
333 return this;
334 }
335
336 private <T> BundleDependencyBuilder setComponentCallbackRef(Cb cbType, Class<T> type, MethodRef<T> ref) {
337 requiresNoStringCallbacks();
338 if (! m_autoConfigInvoked) m_autoConfig = false;
339 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
340 list.add((instance, component, bundle) -> {
341 Object componentImpl = Stream.of(component.getInstances())
342 .filter(impl -> Helpers.getClass(impl).equals(type))
343 .findFirst()
344 .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes."));
345 ref.accept((T) componentImpl, component, bundle);
346 });
347 return this;
348 }
349
350 @SuppressWarnings("unused")
351 private Object createCallbackInstance() {
352 Object cb = null;
353
354 cb = new Object() {
355 void add(Component c, Bundle bundle) {
356 invokeMethodRefs(Cb.ADD, c, bundle);
357 }
358
359 void change(Component c, Bundle bundle) {
360 invokeMethodRefs(Cb.CHG, c, bundle);
361 }
362
363 void remove(Component c, Bundle bundle) {
364 invokeMethodRefs(Cb.REM, c, bundle);
365 }
366 };
367
368 return cb;
369 }
370
371 private void invokeMethodRefs(Cb cbType, Component c, Bundle bundle) {
372 m_refs.computeIfPresent(cbType, (k, mrefs) -> {
373 mrefs.forEach(mref -> mref.accept(null, c, bundle));
374 return mrefs;
375 });
376 }
377
378 private void requiresNoStringCallbacks() {
379 if (m_added != null || m_changed != null || m_removed != null) {
380 throw new IllegalStateException("can't mix method references and string callbacks.");
381 }
382 }
383
384 private void requiresNoMethodRefs() {
385 if (m_refs.size() > 0) {
386 throw new IllegalStateException("can't mix method references and string callbacks.");
387 }
388 }
389}