blob: ab4daf43112cc2e9857b68fcd0e4913e438fd5ba [file] [log] [blame]
Pierre De Rop3a00a212015-03-01 09:27:46 +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 */
19package org.apache.felix.dm.impl;
20
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Proxy;
23import java.util.AbstractMap;
24import java.util.Arrays;
25import java.util.Dictionary;
26import java.util.HashSet;
27import java.util.Hashtable;
28import java.util.Map;
29import java.util.Set;
30
31import org.apache.felix.dm.Component;
32import org.apache.felix.dm.ComponentDeclaration;
33import org.apache.felix.dm.ServiceDependency;
34import org.apache.felix.dm.context.AbstractDependency;
35import org.apache.felix.dm.context.DependencyContext;
36import org.apache.felix.dm.context.Event;
37import org.apache.felix.dm.context.EventType;
38import org.apache.felix.dm.tracker.ServiceTracker;
39import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
40import org.osgi.framework.BundleContext;
41import org.osgi.framework.Constants;
42import org.osgi.framework.InvalidSyntaxException;
43import org.osgi.framework.ServiceReference;
44
45/**
46 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
47 */
48public class ServiceDependencyImpl extends AbstractDependency<ServiceDependency> implements ServiceDependency, ServiceTrackerCustomizer {
49 protected volatile ServiceTracker m_tracker;
50 protected String m_swap;
51 protected volatile Class<?> m_trackedServiceName;
52 private volatile String m_trackedServiceFilter;
53 private volatile String m_trackedServiceFilterUnmodified;
54 private volatile ServiceReference m_trackedServiceReference;
55 private volatile Object m_defaultImplementation;
56 private volatile Object m_defaultImplementationInstance;
57 private volatile Object m_nullObject;
58 private boolean m_debug = false;
59 private String m_debugKey;
60 private long m_trackedServiceReferenceId;
61
62 public ServiceDependency setDebug(String debugKey) {
63 m_debugKey = debugKey;
64 m_debug = true;
65 return this;
66 }
67
68 /**
69 * Entry to wrap service properties behind a Map.
70 */
71 private static final class ServicePropertiesMapEntry implements Map.Entry<String, Object> {
72 private final String m_key;
73 private Object m_value;
74
75 public ServicePropertiesMapEntry(String key, Object value) {
76 m_key = key;
77 m_value = value;
78 }
79
80 public String getKey() {
81 return m_key;
82 }
83
84 public Object getValue() {
85 return m_value;
86 }
87
88 public String toString() {
89 return m_key + "=" + m_value;
90 }
91
92 public Object setValue(Object value) {
93 Object oldValue = m_value;
94 m_value = value;
95 return oldValue;
96 }
97
98 @SuppressWarnings("unchecked")
99 public boolean equals(Object o) {
100 if (!(o instanceof Map.Entry)) {
101 return false;
102 }
103 Map.Entry<String, Object> e = (Map.Entry<String, Object>) o;
104 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
105 }
106
107 public int hashCode() {
108 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
109 }
110
111 private static final boolean eq(Object o1, Object o2) {
112 return (o1 == null ? o2 == null : o1.equals(o2));
113 }
114 }
115
116 /**
117 * Wraps service properties behind a Map.
118 */
119 private final static class ServicePropertiesMap extends AbstractMap<String, Object> {
120 private final ServiceReference m_ref;
121
122 public ServicePropertiesMap(ServiceReference ref) {
123 m_ref = ref;
124 }
125
126 public Object get(Object key) {
127 return m_ref.getProperty(key.toString());
128 }
129
130 public int size() {
131 return m_ref.getPropertyKeys().length;
132 }
133
134 public Set<Map.Entry<String, Object>> entrySet() {
135 Set<Map.Entry<String, Object>> set = new HashSet<>();
136 String[] keys = m_ref.getPropertyKeys();
137 for (int i = 0; i < keys.length; i++) {
138 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
139 }
140 return set;
141 }
142 }
143
144 public ServiceDependencyImpl() {
145 }
146
147 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
148 super(prototype);
149 m_trackedServiceName = prototype.m_trackedServiceName;
150 m_nullObject = prototype.m_nullObject;
151 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
152 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
153 m_trackedServiceReference = prototype.m_trackedServiceReference;
154 m_autoConfigInstance = prototype.m_autoConfigInstance;
155 m_defaultImplementation = prototype.m_defaultImplementation;
156 m_autoConfig = prototype.m_autoConfig;
157 }
158
159 // --- CREATION
160
161 public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
162 setCallbacks(instance, added, changed, removed);
163 m_swap = swapped;
164 return this;
165 }
166
167 public ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
168 setCallbacks(added, changed, removed);
169 m_swap = swapped;
170 return this;
171 }
172
173 @Override
174 public ServiceDependency setDefaultImplementation(Object implementation) {
175 ensureNotActive();
176 m_defaultImplementation = implementation;
177 return this;
178 }
179
180 @Override
181 public ServiceDependency setService(Class<?> serviceName) {
182 setService(serviceName, null, null);
183 return this;
184 }
185
186 public ServiceDependency setService(Class<?> serviceName, String serviceFilter) {
187 setService(serviceName, null, serviceFilter);
188 return this;
189 }
190
191 public ServiceDependency setService(String serviceFilter) {
192 if (serviceFilter == null) {
193 throw new IllegalArgumentException("Service filter cannot be null.");
194 }
195 setService(null, null, serviceFilter);
196 return this;
197 }
198
199 public ServiceDependency setService(Class<?> serviceName, ServiceReference serviceReference) {
200 setService(serviceName, serviceReference, null);
201 return this;
202 }
203
204 @Override
205 public void start() {
206 if (m_trackedServiceName != null) {
207 BundleContext ctx = m_component.getBundleContext();
208 if (m_trackedServiceFilter != null) {
209 try {
210 m_tracker = new ServiceTracker(ctx, ctx.createFilter(m_trackedServiceFilter), this);
211 } catch (InvalidSyntaxException e) {
212 throw new IllegalStateException("Invalid filter definition for dependency: "
213 + m_trackedServiceFilter);
214 }
215 } else if (m_trackedServiceReference != null) {
216 m_tracker = new ServiceTracker(ctx, m_trackedServiceReference, this);
217 } else {
218 m_tracker = new ServiceTracker(ctx, m_trackedServiceName.getName(), this);
219 }
220 } else {
221 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
222 }
223 if (m_debug) {
224 m_tracker.setDebug(m_debugKey);
225 }
226 m_tracker.open();
227 super.start();
228 }
229
230 @Override
231 public void stop() {
232 m_tracker.close();
233 m_tracker = null;
234 super.stop();
235 }
236
237 @Override
238 public Object addingService(ServiceReference reference) {
239 try {
240 return m_component.getBundleContext().getService(reference);
241 } catch (IllegalStateException e) {
242 // most likely our bundle is being stopped. Only log an exception if our component is enabled.
243 if (m_component.isActive()) {
244 m_component.getLogger().warn("could not handle service dependency for component %s", e,
245 m_component.getComponentDeclaration().getClassName());
246 }
247 return null;
248 }
249 }
250
251 @Override
252 public void addedService(ServiceReference reference, Object service) {
253 if (m_debug) {
254 System.out.println(m_debugKey + " addedService: ref=" + reference + ", service=" + service);
255 }
256 m_component.handleEvent(this, EventType.ADDED,
257 new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
258 }
259
260 @Override
261 public void modifiedService(ServiceReference reference, Object service) {
262 m_component.handleEvent(this, EventType.CHANGED,
263 new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
264 }
265
266 @Override
267 public void removedService(ServiceReference reference, Object service) {
268 m_component.handleEvent(this, EventType.REMOVED,
269 new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service));
270 }
271
272 @Override
273 public void invokeCallback(EventType type, Event ... events) {
274 switch (type) {
275 case ADDED:
276 if (m_add != null) {
277 invoke (m_add, events[0], getInstances());
278 }
279 break;
280 case CHANGED:
281 if (m_change != null) {
282 invoke (m_change, events[0], getInstances());
283 }
284 break;
285 case REMOVED:
286 if (m_remove != null) {
287 invoke (m_remove, events[0], getInstances());
288 }
289 break;
290 case SWAPPED:
291 if (m_swap != null) {
292 ServiceEventImpl oldE = (ServiceEventImpl) events[0];
293 ServiceEventImpl newE = (ServiceEventImpl) events[1];
294 invokeSwap(m_swap, oldE.getReference(), oldE.getEvent(), newE.getReference(), newE.getEvent(),
295 getInstances());
296 }
297 break;
298 }
299 }
300
301 @Override
302 public Class<?> getAutoConfigType() {
303 return m_trackedServiceName;
304 }
305
306 @Override
307 public DependencyContext createCopy() {
308 return new ServiceDependencyImpl(this);
309 }
310
311 @Override
312 public String getName() {
313 StringBuilder sb = new StringBuilder();
314 if (m_trackedServiceName != null) {
315 sb.append(m_trackedServiceName.getName());
316 if (m_trackedServiceFilterUnmodified != null) {
317 sb.append(' ');
318 sb.append(m_trackedServiceFilterUnmodified);
319 }
320 }
321 if (m_trackedServiceReference != null) {
322 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID) + "}");
323 }
324 return sb.toString();
325 }
326
327 @Override
328 public String getSimpleName() {
329 if (m_trackedServiceName != null) {
330 return m_trackedServiceName.getName();
331 }
332 return null;
333 }
334
335 @Override
336 public String getFilter() {
337 if (m_trackedServiceFilterUnmodified != null) {
338 return m_trackedServiceFilterUnmodified;
339 } else if (m_trackedServiceReference != null) {
340 return new StringBuilder("(").append(Constants.SERVICE_ID).append("=").append(
341 String.valueOf(m_trackedServiceReferenceId)).append(")").toString();
342 } else {
343 return null;
344 }
345 }
346
347 @Override
348 public String getType() {
349 return "service";
350 }
351
352 @SuppressWarnings("unchecked")
353 @Override
354 public Dictionary<String, Object> getProperties() {
355 ServiceEventImpl se = (ServiceEventImpl) m_component.getDependencyEvent(this);
356 if (se != null) {
357 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
358 try {
359 return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
360 new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
361 {se.getReference(), se.getEvent()}, {se.getReference()}});
362 } catch (InvocationTargetException e) {
363 m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
364 } catch (Throwable e) {
365 m_component.getLogger().warn("Exception while trying to invoke callback method", e);
366 }
367 throw new IllegalStateException("Could not invoke callback");
368 } else {
369 Hashtable<String, Object> props = new Hashtable<>();
370 String[] keys = se.getReference().getPropertyKeys();
371 for (int i = 0; i < keys.length; i++) {
372 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
373 props.put(keys[i], se.getReference().getProperty(keys[i]));
374 }
375 }
376 return props;
377 }
378 } else {
379 throw new IllegalStateException("cannot find service reference");
380 }
381 }
382
383 /** Internal method to set the name, service reference and/or filter. */
384 private void setService(Class<?> serviceName, ServiceReference serviceReference, String serviceFilter) {
385 ensureNotActive();
386 if (serviceName == null) {
387 m_trackedServiceName = Object.class;
388 }
389 else {
390 m_trackedServiceName = serviceName;
391 }
392 if (serviceFilter != null) {
393 m_trackedServiceFilterUnmodified = serviceFilter;
394 if (serviceName == null) {
395 m_trackedServiceFilter = serviceFilter;
396 }
397 else {
398 m_trackedServiceFilter = "(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")"
399 + serviceFilter + ")";
400 }
401 }
402 else {
403 m_trackedServiceFilterUnmodified = null;
404 m_trackedServiceFilter = null;
405 }
406 if (serviceReference != null) {
407 m_trackedServiceReference = serviceReference;
408 if (serviceFilter != null) {
409 throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
410 }
411 m_trackedServiceReferenceId = (Long) m_trackedServiceReference.getProperty(Constants.SERVICE_ID);
412 }
413 else {
414 m_trackedServiceReference = null;
415 }
416 }
417
418 @Override
419 public Object getDefaultService(boolean nullObject) {
420 Object service = null;
421 if (isAutoConfig()) {
422 service = getDefaultImplementation();
423 if (service == null && nullObject) {
424 service = getNullObject();
425 }
426 }
427 return service;
428 }
429
430 private Object getNullObject() {
431 if (m_nullObject == null) {
432 Class<?> trackedServiceName;
433 trackedServiceName = m_trackedServiceName;
434 try {
435 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(),
436 new Class[] { trackedServiceName }, new DefaultNullObject());
437 }
438 catch (Throwable err) {
439 m_component.getLogger().err("Could not create null object for %s.", err, trackedServiceName);
440 }
441 }
442 return m_nullObject;
443 }
444
445 private Object getDefaultImplementation() {
446 if (m_defaultImplementation != null) {
447 if (m_defaultImplementation instanceof Class) {
448 try {
449 m_defaultImplementationInstance = ((Class<?>) m_defaultImplementation).newInstance();
450 }
451 catch (Throwable e) {
452 m_component.getLogger().err("Could not create default implementation instance of class %s.", e,
453 m_defaultImplementation);
454 }
455 }
456 else {
457 m_defaultImplementationInstance = m_defaultImplementation;
458 }
459 }
460 return m_defaultImplementationInstance;
461 }
462
463 public void invoke(String method, Event e, Object[] instances) {
464 ServiceEventImpl se = (ServiceEventImpl) e;
465 ServicePropertiesMap propertiesMap = new ServicePropertiesMap(se.getReference());
466 Dictionary<?,?> properties = se.getProperties();
467 m_component.invokeCallbackMethod(instances, method,
468 new Class[][]{
469 {Component.class, ServiceReference.class, m_trackedServiceName},
470 {Component.class, ServiceReference.class, Object.class},
471 {Component.class, ServiceReference.class},
472 {Component.class, m_trackedServiceName},
473 {Component.class, Object.class},
474 {Component.class},
475 {Component.class, Map.class, m_trackedServiceName},
476 {ServiceReference.class, m_trackedServiceName},
477 {ServiceReference.class, Object.class},
478 {ServiceReference.class},
479 {m_trackedServiceName},
480 {m_trackedServiceName, Map.class},
481 {Map.class, m_trackedServiceName},
482 {m_trackedServiceName, Dictionary.class},
483 {Dictionary.class, m_trackedServiceName},
484 {Object.class},
485 {}},
486
487 new Object[][]{
488 {m_component, se.getReference(), se.getEvent()},
489 {m_component, se.getReference(), se.getEvent()},
490 {m_component, se.getReference()},
491 {m_component, se.getEvent()},
492 {m_component, se.getEvent()},
493 {m_component},
494 {m_component, propertiesMap, se.getEvent()},
495 {se.getReference(), se.getEvent()},
496 {se.getReference(), se.getEvent()},
497 {se.getReference()},
498 {se.getEvent()},
499 {se.getEvent(), propertiesMap},
500 {propertiesMap, se.getEvent()},
501 {se.getEvent(), properties},
502 {properties, se.getEvent()},
503 {se.getEvent()},
504 {}}
505 );
506 }
507
508 public void invokeSwap(String swapMethod, ServiceReference previousReference, Object previous,
509 ServiceReference currentReference, Object current, Object[] instances) {
510 if (m_debug) {
511 System.out.println("invoke swap: " + swapMethod + " on component " + m_component + ", instances: " + Arrays.toString(instances) + " - " + ((ComponentDeclaration)m_component).getState());
512 }
513 try {
514 m_component.invokeCallbackMethod(instances, swapMethod,
515 new Class[][]{
516 {m_trackedServiceName, m_trackedServiceName},
517 {Object.class, Object.class},
518 {ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
519 {ServiceReference.class, Object.class, ServiceReference.class, Object.class},
520 {Component.class, m_trackedServiceName, m_trackedServiceName},
521 {Component.class, Object.class, Object.class},
522 {Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
523 {Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class}},
524
525 new Object[][]{
526 {previous, current},
527 {previous, current},
528 {previousReference, previous, currentReference, current},
529 {previousReference, previous, currentReference, current}, {m_component, previous, current},
530 {m_component, previous, current}, {m_component, previousReference, previous, currentReference, current},
531 {m_component, previousReference, previous, currentReference, current}}
532 );
533 } catch (Throwable e) {
534 m_component.getLogger().err("Could not invoke swap callback", e);
535 }
536 }
537
538 @Override
539 public void swappedService(final ServiceReference reference, final Object service,
540 final ServiceReference newReference, final Object newService) {
541 if (m_swap != null) {
542 // it will not trigger a state change, but the actual swap should be scheduled to prevent things
543 // getting out of order.
544 // We delegate the swap handling to the ComponentImpl, which is the class responsible for state management.
545 // The ComponentImpl will first check if the component is in the proper state so the swap method can be invoked.
546 m_component.handleEvent(this, EventType.SWAPPED,
547 new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), reference, service),
548 new ServiceEventImpl(m_component.getBundle(), m_component.getBundleContext(), newReference, newService));
549 } else {
550 addedService(newReference, newService);
551 removedService(reference, service);
552 }
553 }
554}