blob: 2888b725564e129427cac3846a611b2e7cbb8e81 [file] [log] [blame]
Marcel Offermansa962bc92009-11-21 17:59:33 +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 Ropaacb3aa2009-12-04 22:53:23 +000019package org.apache.felix.dm.impl.dependencies;
Marcel Offermansa962bc92009-11-21 17:59:33 +000020
Marcel Offermans26081d32010-07-12 12:43:42 +000021import java.lang.reflect.InvocationTargetException;
Marcel Offermansa962bc92009-11-21 17:59:33 +000022import java.lang.reflect.Proxy;
23import java.util.AbstractMap;
Marcel Offermanse14b3422009-11-25 23:04:32 +000024import java.util.ArrayList;
Marcel Offermansa962bc92009-11-21 17:59:33 +000025import java.util.Arrays;
26import java.util.Comparator;
Marcel Offermans117aa2f2009-12-10 09:48:17 +000027import java.util.Dictionary;
Marcel Offermans09e14aa2010-11-25 13:36:23 +000028import java.util.HashMap;
Marcel Offermansa962bc92009-11-21 17:59:33 +000029import java.util.HashSet;
Marcel Offermans7f0cec02011-11-04 13:39:28 +000030import java.util.Iterator;
Marcel Offermanse14b3422009-11-25 23:04:32 +000031import java.util.List;
Marcel Offermansa962bc92009-11-21 17:59:33 +000032import java.util.Map;
Marcel Offermans7f0cec02011-11-04 13:39:28 +000033import java.util.Map.Entry;
Marcel Offermans26081d32010-07-12 12:43:42 +000034import java.util.Properties;
Marcel Offermansa962bc92009-11-21 17:59:33 +000035import java.util.Set;
36
Marcel Offermans706fb272010-11-15 12:52:58 +000037import org.apache.felix.dm.Component;
Marcel Offermans9e50ef32010-10-07 11:37:00 +000038import org.apache.felix.dm.ComponentDependencyDeclaration;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000039import org.apache.felix.dm.Dependency;
Marcel Offermans3d921212010-08-09 13:37:02 +000040import org.apache.felix.dm.DependencyService;
Marcel Offermans706fb272010-11-15 12:52:58 +000041import org.apache.felix.dm.InvocationUtil;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000042import org.apache.felix.dm.ServiceDependency;
Marcel Offermans7f0cec02011-11-04 13:39:28 +000043import org.apache.felix.dm.ServiceUtil;
Pierre De Ropaacb3aa2009-12-04 22:53:23 +000044import org.apache.felix.dm.impl.DefaultNullObject;
45import org.apache.felix.dm.impl.Logger;
Marcel Offermansfaaed472010-09-08 10:07:32 +000046import org.apache.felix.dm.tracker.ServiceTracker;
47import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
Marcel Offermansa962bc92009-11-21 17:59:33 +000048import org.osgi.framework.BundleContext;
49import org.osgi.framework.Constants;
50import org.osgi.framework.InvalidSyntaxException;
51import org.osgi.framework.ServiceReference;
Marcel Offermans26081d32010-07-12 12:43:42 +000052import org.osgi.service.log.LogService;
Marcel Offermansa962bc92009-11-21 17:59:33 +000053
54/**
55 * Service dependency that can track an OSGi service.
56 *
57 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
58 */
Marcel Offermansfaaed472010-09-08 10:07:32 +000059public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
Marcel Offermanse14b3422009-11-25 23:04:32 +000060 protected List m_services = new ArrayList();
Marcel Offermans74363c32009-11-23 19:56:08 +000061 protected volatile ServiceTracker m_tracker;
62 protected BundleContext m_context;
Marcel Offermans74363c32009-11-23 19:56:08 +000063 protected volatile Class m_trackedServiceName;
Marcel Offermansa962bc92009-11-21 17:59:33 +000064 private Object m_nullObject;
65 private volatile String m_trackedServiceFilter;
66 private volatile String m_trackedServiceFilterUnmodified;
67 private volatile ServiceReference m_trackedServiceReference;
Marcel Offermansa962bc92009-11-21 17:59:33 +000068 private Object m_callbackInstance;
69 private String m_callbackAdded;
70 private String m_callbackChanged;
71 private String m_callbackRemoved;
Marcel Offermans7f0cec02011-11-04 13:39:28 +000072 private String m_callbackSwapped;
Marcel Offermansa962bc92009-11-21 17:59:33 +000073 private boolean m_autoConfig;
Marcel Offermans74363c32009-11-23 19:56:08 +000074 protected ServiceReference m_reference;
75 protected Object m_serviceInstance;
Marcel Offermansa962bc92009-11-21 17:59:33 +000076 private String m_autoConfigInstance;
77 private boolean m_autoConfigInvoked;
78 private Object m_defaultImplementation;
79 private Object m_defaultImplementationInstance;
Marcel Offermansb196d722009-11-26 17:12:12 +000080 private boolean m_isAvailable;
Marcel Offermans26081d32010-07-12 12:43:42 +000081 private boolean m_propagate;
82 private Object m_propagateCallbackInstance;
83 private String m_propagateCallbackMethod;
Marcel Offermans7784e402011-01-21 20:41:50 +000084 private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
Marcel Offermans7f0cec02011-11-04 13:39:28 +000085 private Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
Marcel Offermansa962bc92009-11-21 17:59:33 +000086
87 private static final Comparator COMPARATOR = new Comparator() {
88 public int getRank(ServiceReference ref) {
89 Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
90 if (ranking != null && (ranking instanceof Integer)) {
91 return ((Integer) ranking).intValue();
92 }
93 return 0;
94 }
95
96 public int compare(Object a, Object b) {
97 ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
98 int ranka = getRank(ra);
99 int rankb = getRank(rb);
100 if (ranka < rankb) {
101 return -1;
102 }
103 else if (ranka > rankb) {
104 return 1;
105 }
106 return 0;
107 }
108 };
109
Marcel Offermans7784e402011-01-21 20:41:50 +0000110 private static final class Tuple /* <ServiceReference, Object> */ {
111 private final ServiceReference m_serviceReference;
112 private final Object m_service;
113
114 public Tuple(ServiceReference first, Object last) {
115 m_serviceReference = first;
116 m_service = last;
117 }
118
119 public ServiceReference getServiceReference() {
120 return m_serviceReference;
121 }
122
123 public Object getService() {
124 return m_service;
125 }
126
127 public boolean equals(Object obj) {
128 return ((Tuple) obj).getServiceReference().equals(getServiceReference());
129 }
130
131 public int hashCode() {
132 return m_serviceReference.hashCode();
133 }
134 }
135
Marcel Offermansa962bc92009-11-21 17:59:33 +0000136 /**
137 * Entry to wrap service properties behind a Map.
138 */
Marcel Offermans7784e402011-01-21 20:41:50 +0000139 private static final class ServicePropertiesMapEntry implements Map.Entry {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000140 private final String m_key;
141 private Object m_value;
142
143 public ServicePropertiesMapEntry(String key, Object value) {
144 m_key = key;
145 m_value = value;
146 }
147
148 public Object getKey() {
149 return m_key;
150 }
151
152 public Object getValue() {
153 return m_value;
154 }
155
156 public String toString() {
157 return m_key + "=" + m_value;
158 }
159
160 public Object setValue(Object value) {
161 Object oldValue = m_value;
162 m_value = value;
163 return oldValue;
164 }
165
166 public boolean equals(Object o) {
167 if (!(o instanceof Map.Entry)) {
168 return false;
169 }
170 Map.Entry e = (Map.Entry) o;
171 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
172 }
173
174 public int hashCode() {
175 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
176 }
177
178 private static final boolean eq(Object o1, Object o2) {
179 return (o1 == null ? o2 == null : o1.equals(o2));
180 }
181 }
182
183 /**
184 * Wraps service properties behind a Map.
185 */
186 private final static class ServicePropertiesMap extends AbstractMap {
187 private final ServiceReference m_ref;
188
189 public ServicePropertiesMap(ServiceReference ref) {
190 m_ref = ref;
191 }
192
193 public Object get(Object key) {
194 return m_ref.getProperty(key.toString());
195 }
196
197 public int size() {
198 return m_ref.getPropertyKeys().length;
199 }
200
201 public Set entrySet() {
202 Set set = new HashSet();
203 String[] keys = m_ref.getPropertyKeys();
204 for (int i = 0; i < keys.length; i++) {
205 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
206 }
207 return set;
208 }
209 }
210
211 /**
212 * Creates a new service dependency.
213 *
214 * @param context the bundle context
215 * @param logger the logger
216 */
Pierre De Ropaacb3aa2009-12-04 22:53:23 +0000217 public ServiceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +0000218 super(logger);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000219 m_context = context;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000220 m_autoConfig = true;
221 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000222
Marcel Offermansb1959f42010-07-01 12:23:51 +0000223 /** Copying constructor that clones an existing instance. */
224 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
225 super(prototype);
Marcel Offermanse7baff52010-12-17 08:51:52 +0000226 synchronized (prototype) {
227 m_context = prototype.m_context;
228 m_autoConfig = prototype.m_autoConfig;
229 m_trackedServiceName = prototype.m_trackedServiceName;
230 m_nullObject = prototype.m_nullObject;
231 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
232 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
233 m_trackedServiceReference = prototype.m_trackedServiceReference;
234 m_callbackInstance = prototype.m_callbackInstance;
235 m_callbackAdded = prototype.m_callbackAdded;
236 m_callbackChanged = prototype.m_callbackChanged;
237 m_callbackRemoved = prototype.m_callbackRemoved;
Xander Uiterlinden30303d62012-07-11 14:19:33 +0000238 m_callbackSwapped = prototype.m_callbackSwapped;
Marcel Offermanse7baff52010-12-17 08:51:52 +0000239 m_autoConfigInstance = prototype.m_autoConfigInstance;
240 m_defaultImplementation = prototype.m_defaultImplementation;
241 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000242 }
243
244 public Dependency createCopy() {
245 return new ServiceDependencyImpl(this);
246 }
247
Marcel Offermansa962bc92009-11-21 17:59:33 +0000248 public synchronized boolean isAutoConfig() {
249 return m_autoConfig;
250 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000251
Marcel Offermansb196d722009-11-26 17:12:12 +0000252 public synchronized boolean isAvailable() {
253 return m_isAvailable;
254 }
255
Marcel Offermansa962bc92009-11-21 17:59:33 +0000256 public synchronized Object getService() {
257 Object service = null;
258 if (m_isStarted) {
259 service = m_tracker.getService();
260 }
Marcel Offermans001db052009-12-08 08:58:40 +0000261 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000262 service = getDefaultImplementation();
263 if (service == null) {
264 service = getNullObject();
265 }
266 }
267 return service;
268 }
269
270 public Object lookupService() {
271 Object service = null;
272 if (m_isStarted) {
Marcel Offermans001db052009-12-08 08:58:40 +0000273 service = getService();
Marcel Offermansa962bc92009-11-21 17:59:33 +0000274 }
275 else {
276 ServiceReference[] refs = null;
277 ServiceReference ref = null;
278 if (m_trackedServiceName != null) {
279 if (m_trackedServiceFilter != null) {
280 try {
281 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
282 if (refs != null) {
283 Arrays.sort(refs, COMPARATOR);
284 ref = refs[0];
285 }
286 }
287 catch (InvalidSyntaxException e) {
288 throw new IllegalStateException("Invalid filter definition for dependency.");
289 }
290 }
291 else if (m_trackedServiceReference != null) {
292 ref = m_trackedServiceReference;
293 }
294 else {
295 ref = m_context.getServiceReference(m_trackedServiceName.getName());
296 }
297 if (ref != null) {
298 service = m_context.getService(ref);
299 }
300 }
301 else {
302 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
303 }
304 }
Marcel Offermans001db052009-12-08 08:58:40 +0000305 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000306 service = getDefaultImplementation();
307 if (service == null) {
308 service = getNullObject();
309 }
310 }
311 return service;
312 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000313
Marcel Offermanse14b3422009-11-25 23:04:32 +0000314 // TODO lots of duplication in lookupService()
315 public ServiceReference lookupServiceReference() {
316 ServiceReference service = null;
317 if (m_isStarted) {
318 service = m_tracker.getServiceReference();
319 }
320 else {
321 ServiceReference[] refs = null;
322 ServiceReference ref = null;
323 if (m_trackedServiceName != null) {
324 if (m_trackedServiceFilter != null) {
325 try {
326 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
327 if (refs != null) {
328 Arrays.sort(refs, COMPARATOR);
329 ref = refs[0];
330 }
331 }
332 catch (InvalidSyntaxException e) {
333 throw new IllegalStateException("Invalid filter definition for dependency.");
334 }
335 }
336 else if (m_trackedServiceReference != null) {
337 ref = m_trackedServiceReference;
338 }
339 else {
340 ref = m_context.getServiceReference(m_trackedServiceName.getName());
341 }
342 if (ref != null) {
343 service = ref;
344 }
345 }
346 else {
347 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
348 }
349 }
350 return service;
351 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000352
353 private Object getNullObject() {
354 if (m_nullObject == null) {
355 Class trackedServiceName;
356 synchronized (this) {
357 trackedServiceName = m_trackedServiceName;
358 }
359 try {
360 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
361 }
362 catch (Exception e) {
363 m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
364 }
365 }
366 return m_nullObject;
367 }
368
369 private Object getDefaultImplementation() {
370 if (m_defaultImplementation != null) {
371 if (m_defaultImplementation instanceof Class) {
372 try {
373 m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
374 }
375 catch (Exception e) {
376 m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
377 }
378 }
379 else {
380 m_defaultImplementationInstance = m_defaultImplementation;
381 }
382 }
383 return m_defaultImplementationInstance;
384 }
385
386 public synchronized Class getInterface() {
387 return m_trackedServiceName;
388 }
389
Marcel Offermanse14b3422009-11-25 23:04:32 +0000390 public void start(DependencyService service) {
391 boolean needsStarting = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000392 synchronized (this) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000393 m_services.add(service);
394 if (!m_isStarted) {
395 if (m_trackedServiceName != null) {
396 if (m_trackedServiceFilter != null) {
397 try {
398 m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
399 }
400 catch (InvalidSyntaxException e) {
Marcel Offermansad760672010-03-03 15:30:01 +0000401 throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000402 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000403 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000404 else if (m_trackedServiceReference != null) {
405 m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000406 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000407 else {
408 m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
409 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000410 }
411 else {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000412 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000413 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000414 m_isStarted = true;
415 needsStarting = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000416 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000417 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000418 if (needsStarting) {
419 m_tracker.open();
420 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000421 }
422
Marcel Offermanse14b3422009-11-25 23:04:32 +0000423 public void stop(DependencyService service) {
424 boolean needsStopping = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000425 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000426 if (m_services.size() == 1 && m_services.contains(service)) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000427 m_isStarted = false;
428 needsStopping = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000429 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000430 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000431 if (needsStopping) {
432 m_tracker.close();
433 m_tracker = null;
434 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000435 //moved this down
436 synchronized (this) {
437 m_services.remove(service);
438 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000439 }
440
441 public Object addingService(ServiceReference ref) {
442 Object service = m_context.getService(ref);
443 // first check to make sure the service is actually an instance of our service
444 if (!m_trackedServiceName.isInstance(service)) {
445 return null;
446 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000447 return service;
448 }
449
450 public void addedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000451 boolean makeAvailable = makeAvailable();
452
Marcel Offermans203bdad2009-12-04 09:23:04 +0000453 Object[] services;
454 synchronized (this) {
455 services = m_services.toArray();
456 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000457 for (int i = 0; i < services.length; i++) {
458 DependencyService ds = (DependencyService) services[i];
459 if (makeAvailable) {
Marcel Offermansfea14842010-10-14 14:45:36 +0000460 if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
461 invokeAdded(ds, ref, service);
462 }
Pierre De Rop1785c332010-06-11 06:03:53 +0000463 // The dependency callback will be defered until all required dependency are available.
464 ds.dependencyAvailable(this);
465 if (!isRequired()) {
466 // For optional dependency, we always invoke callback, because at this point, we know
467 // that the service has been started, and the service start method has been called.
468 // (See the ServiceImpl.bindService method, which will activate optional dependencies using
469 // startTrackingOptional() method).
Marcel Offermanse14b3422009-11-25 23:04:32 +0000470 invokeAdded(ds, ref, service);
471 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000472 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000473 else {
474 ds.dependencyChanged(this);
Pierre De Rop1785c332010-06-11 06:03:53 +0000475 // At this point, either the dependency is optional (meaning that the service has been started,
476 // because if not, then our dependency would not be active); or the dependency is required,
477 // meaning that either the service is not yet started, or already started.
478 // In all cases, we have to inject the required dependency.
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000479
480 // we only try to invoke the method here if we are really already instantiated
481 if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
482 invokeAdded(ds, ref, service);
483 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000484 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000485 }
486 }
487
Marcel Offermansa962bc92009-11-21 17:59:33 +0000488 public void modifiedService(ServiceReference ref, Object service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000489 Object[] services;
490 synchronized (this) {
491 services = m_services.toArray();
492 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000493 for (int i = 0; i < services.length; i++) {
494 DependencyService ds = (DependencyService) services[i];
495 ds.dependencyChanged(this);
496 if (ds.isRegistered()) {
497 invokeChanged(ds, ref, service);
498 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000499 }
500 }
501
Marcel Offermansa962bc92009-11-21 17:59:33 +0000502 public void removedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000503 boolean makeUnavailable = makeUnavailable();
504
Marcel Offermans203bdad2009-12-04 09:23:04 +0000505 Object[] services;
506 synchronized (this) {
507 services = m_services.toArray();
508 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000509
Marcel Offermanse14b3422009-11-25 23:04:32 +0000510 for (int i = 0; i < services.length; i++) {
511 DependencyService ds = (DependencyService) services[i];
512 if (makeUnavailable) {
513 ds.dependencyUnavailable(this);
Marcel Offermansfea14842010-10-14 14:45:36 +0000514 if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000515 invokeRemoved(ds, ref, service);
516 }
517 }
518 else {
519 ds.dependencyChanged(this);
520 invokeRemoved(ds, ref, service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000521 }
522 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000523 // unget what we got in addingService (see ServiceTracker 701.4.1)
524 m_context.ungetService(ref);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000525
Marcel Offermansa962bc92009-11-21 17:59:33 +0000526 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000527
Marcel Offermansea89b862010-06-24 13:14:43 +0000528 public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +0000529 boolean added = false;
530 synchronized (m_sr) {
531 Set set = (Set) m_sr.get(dependencyService);
532 if (set == null) {
533 set = new HashSet();
534 m_sr.put(dependencyService, set);
535 }
Marcel Offermans7784e402011-01-21 20:41:50 +0000536 added = set.add(new Tuple(reference, service));
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000537 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000538 if (added) {
539 // when a changed callback is specified we might not call the added callback just yet
540 if (m_callbackSwapped != null) {
541 handleAspectAwareAdded(dependencyService, reference, service);
Marcel Offermans4aa032c2012-01-02 19:18:22 +0000542 }
543 else {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000544 invoke(dependencyService, reference, service, m_callbackAdded);
545 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000546 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000547 }
548
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000549 private void handleAspectAwareAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
550 if (componentIsDependencyManagerFactory(dependencyService)) {
551 // component is either aspect or adapter factory instance, these must be ignored.
552 return;
553 }
554 boolean invokeAdded = false;
555 Integer ranking = ServiceUtil.getRankingAsInteger(reference);
556 Tuple highestRankedService = null;
557 synchronized (m_componentByRank) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000558 Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
559 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
560 if (componentMap == null) {
561 // create new componentMap
562 componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
563 m_componentByRank.put(dependencyService, componentMap);
564 }
565 Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
566 if (rankings == null) {
567 // new component added
568 rankings = new HashMap(); /* <Integer, Tuple> */
569 componentMap.put(originalServiceId, rankings);
570 rankings.put(ranking, new Tuple(reference, service));
571 invokeAdded = true;
572 }
573
574 if (!invokeAdded) {
575 highestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
576 }
577 }
578 if (invokeAdded) {
579 invoke(dependencyService, reference, service, m_callbackAdded);
580 } else {
581 invokeSwappedCallback(dependencyService, highestRankedService.getServiceReference(), highestRankedService.getService(), reference, service);
582 }
583 }
584
Marcel Offermans4aa032c2012-01-02 19:18:22 +0000585 private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
586 Object component = dependencyService.getService();
587 if (component != null) {
588 String className = component.getClass().getName();
589 return className.startsWith("org.apache.felix.dm")
590 && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
591 && !className.startsWith("org.apache.felix.dm.test");
592 }
593 return false;
594 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000595
596 private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference, Object newService, Integer newRanking) {
597 // does a component with a higher ranking exists
598 synchronized (m_componentByRank) {
599 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
600 Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
601 Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
602 rankings.remove(highestEntry.getKey());
603 rankings.put(newRanking, new Tuple(newReference, newService));
604 return (Tuple) highestEntry.getValue();
605 }
606 }
607
608 private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
609 Entry highestEntry = null; /* <Integer, Tuple> */
610 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
611 Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
612 if (rankings != null) {
613 for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) { /* <Integer, Tuple> */
614 Entry mapEntry = (Entry) entryIterator.next();
615 if (highestEntry == null) {
616 highestEntry = mapEntry;
617 } else {
618 if (((Integer)mapEntry.getKey()).intValue() > ((Integer)highestEntry.getKey()).intValue()) {
619 highestEntry = mapEntry;
620 }
621 }
622 }
623 }
624 return highestEntry;
625 }
626
627
628
629 private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
630 // get the collection of rankings
631 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
632
633 Map rankings = null; /* <Integer, Tuple> */
634 if (componentMap != null) {
635 rankings = (Map) componentMap.get(serviceId);
636 }
637 // if there is only one element left in the collection of rankings
638 // and this last element has the same ranking as the supplied service (in other words, it is the same)
639 // then this is the last service
640 // NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
641 // because an aspect on top of the original service is being removed (but the original service is still
642 // there). That in turn triggers:
643 // 1) a call to added(original-service)
644 // 2) that causes a swap
645 // 3) a call to removed(aspect-service) <-- that's what we're talking about
646 return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry)rankings.entrySet().iterator().next()).getKey()
647 .equals(ServiceUtil.getRankingAsInteger(reference)));
648 }
649
650
Marcel Offermansea89b862010-06-24 13:14:43 +0000651 public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
652 invoke(dependencyService, reference, service, m_callbackChanged);
653 }
654
Marcel Offermansb196d722009-11-26 17:12:12 +0000655 public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +0000656 boolean removed = false;
657 synchronized (m_sr) {
658 Set set = (Set) m_sr.get(dependencyService);
Marcel Offermans7784e402011-01-21 20:41:50 +0000659 removed = (set != null && set.remove(new Tuple(reference, service)));
Marcel Offermanse7baff52010-12-17 08:51:52 +0000660 }
661 if (removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000662 if (m_callbackSwapped != null) {
663 handleAspectAwareRemoved(dependencyService, reference, service);
Marcel Offermans4aa032c2012-01-02 19:18:22 +0000664 }
665 else {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000666 invoke(dependencyService, reference, service, m_callbackRemoved);
667 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000668 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000669 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000670
671 private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
672 if (componentIsDependencyManagerFactory(dependencyService)) {
673 // component is either aspect or adapter factory instance, these must be ignored.
674 return;
675 }
676 Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
677 synchronized (m_componentByRank) {
678 if (isLastService(dependencyService, reference, service, serviceId)) {
679 invoke(dependencyService, reference, service, m_callbackRemoved);
680 }
681 Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
682 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
683 if (componentMap != null) {
684 Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
685 for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) {
686 Entry mapEntry = (Entry) entryIterator.next();
687 if (((Tuple)mapEntry.getValue()).getServiceReference().equals(reference)) {
688 // remove the reference
689 rankings.remove(mapEntry.getKey());
690 }
691 }
692 if (rankings.size() == 0) {
693 componentMap.remove(originalServiceId);
694 }
695 if (componentMap.size() == 0) {
696 m_componentByRank.remove(dependencyService);
697 }
698 }
699 }
700 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000701
702 public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
703 if (name != null) {
704 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
Marcel Offermans706fb272010-11-15 12:52:58 +0000705 new Class[][] {
706 {Component.class, ServiceReference.class, m_trackedServiceName}, {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class}, {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class}, {Component.class, Map.class, m_trackedServiceName},
707 {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
708 },
709 new Object[][] {
710 {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
711 {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
712 }
Marcel Offermansb196d722009-11-26 17:12:12 +0000713 );
Marcel Offermansa962bc92009-11-21 17:59:33 +0000714 }
715 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000716
717 private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous, ServiceReference currentServiceReference,
718 Object current) {
Xander Uiterlinden17919992012-05-03 07:16:53 +0000719 // sanity check on the service references
720 Integer oldRank = (Integer) previousReference.getProperty(Constants.SERVICE_RANKING);
721 Integer newRank = (Integer) currentServiceReference.getProperty(Constants.SERVICE_RANKING);
722
723 if (oldRank != null && newRank != null && oldRank.equals(newRank)) {
724 throw new IllegalStateException("Attempt to swap a service for a service with the same rank! previousReference: " + previousReference + ", currentReference: " + currentServiceReference);
725 }
726
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000727 component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][] { { m_trackedServiceName, m_trackedServiceName },
728 { Object.class, Object.class }, { ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
729 { ServiceReference.class, Object.class, ServiceReference.class, Object.class } }, new Object[][] { { previous, current },
730 { previous, current }, { previousReference, previous, currentServiceReference, current },
731 { previousReference, previous, currentServiceReference, current } });
732 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000733
Marcel Offermans74363c32009-11-23 19:56:08 +0000734 protected synchronized boolean makeAvailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000735 if (!isAvailable()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000736 m_isAvailable = true;
737 return true;
738 }
739 return false;
740 }
741
742 private synchronized boolean makeUnavailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000743 if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000744 m_isAvailable = false;
745 return true;
746 }
747 return false;
748 }
749
Marcel Offermanse14b3422009-11-25 23:04:32 +0000750 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
751 if (m_callbackInstance == null) {
752 return dependencyService.getCompositionInstances();
753 }
754 else {
755 return new Object[] { m_callbackInstance };
756 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000757 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000758
759 // ----- CREATION
760
761 /**
762 * Sets the name of the service that should be tracked.
763 *
764 * @param serviceName the name of the service
765 * @return this service dependency
766 */
767 public synchronized ServiceDependency setService(Class serviceName) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000768 setService(serviceName, null, null);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000769 return this;
770 }
771
772 /**
773 * Sets the name of the service that should be tracked. You can either specify
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000774 * only the name, only the filter, or the name and a filter.
775 * <p>
776 * If you specify name and filter, the filter is used
Marcel Offermansa962bc92009-11-21 17:59:33 +0000777 * to track the service and should only return services of the type that was specified
778 * in the name. To make sure of this, the filter is actually extended internally to
779 * filter on the correct name.
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000780 * <p>
781 * If you specify only the filter, the name is assumed to be a service of type
782 * <code>Object</code> which means that, when auto configuration is on, instances
783 * of that service will be injected in any field of type <code>Object</code>.
Marcel Offermansa962bc92009-11-21 17:59:33 +0000784 *
785 * @param serviceName the name of the service
786 * @param serviceFilter the filter condition
787 * @return this service dependency
788 */
789 public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000790 setService(serviceName, null, serviceFilter);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000791 return this;
792 }
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000793
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000794 /**
795 * Sets the name of the service that should be tracked. The name is assumed to be
796 * a service of type <code>Object</code> which means that, when auto configuration
797 * is on, instances of that service will be injected in any field of type
798 * <code>Object</code>.
799 *
800 * @param serviceFilter the filter condition
801 * @return this service dependency
802 */
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000803 public synchronized ServiceDependency setService(String serviceFilter) {
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000804 if (serviceFilter == null) {
805 throw new IllegalArgumentException("Service filter cannot be null.");
806 }
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000807 setService(null, null, serviceFilter);
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000808 return this;
809 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000810
811 /**
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000812 * Sets the name of the service that should be tracked. By specifying the service
813 * reference of the service you want to track, you can directly track a single
814 * service. The name you use must match the type of service referred to by the
815 * service reference and it is up to you to make sure that is the case.
Marcel Offermansa962bc92009-11-21 17:59:33 +0000816 *
817 * @param serviceName the name of the service
818 * @param serviceReference the service reference to track
819 * @return this service dependency
820 */
821 public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000822 setService(serviceName, serviceReference, null);
823 return this;
824 }
825
826 /** Internal method to set the name, service reference and/or filter. */
827 private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000828 ensureNotActive();
829 if (serviceName == null) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000830 m_trackedServiceName = Object.class;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000831 }
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000832 else {
833 m_trackedServiceName = serviceName;
834 }
835 if (serviceFilter != null) {
836 m_trackedServiceFilterUnmodified = serviceFilter;
837 if (serviceName == null) {
838 m_trackedServiceFilter = serviceFilter;
839 }
840 else {
841 m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
842 }
843 }
844 else {
845 m_trackedServiceFilterUnmodified = null;
846 m_trackedServiceFilter = null;
847 }
848 if (serviceReference != null) {
849 m_trackedServiceReference = serviceReference;
850 if (serviceFilter != null) {
851 throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
852 }
853 }
854 else {
855 m_trackedServiceReference = null;
856 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000857 }
858
859 /**
860 * Sets the default implementation for this service dependency. You can use this to supply
861 * your own implementation that will be used instead of a Null Object when the dependency is
862 * not available. This is also convenient if the service dependency is not an interface
863 * (which would cause the Null Object creation to fail) but a class.
864 *
865 * @param implementation the instance to use or the class to instantiate if you want to lazily
866 * instantiate this implementation
867 * @return this service dependency
868 */
869 public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
870 ensureNotActive();
871 m_defaultImplementation = implementation;
872 return this;
873 }
874
875 /**
876 * Sets the required flag which determines if this service is required or not.
877 *
878 * @param required the required flag
879 * @return this service dependency
880 */
881 public synchronized ServiceDependency setRequired(boolean required) {
882 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000883 setIsRequired(required);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000884 return this;
885 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000886
887 public ServiceDependency setInstanceBound(boolean isInstanceBound) {
Marcel Offermans61a81142010-04-02 15:16:50 +0000888 setIsInstanceBound(isInstanceBound);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000889 return this;
890 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000891
892 /**
893 * Sets auto configuration for this service. Auto configuration allows the
894 * dependency to fill in any attributes in the service implementation that
895 * are of the same type as this dependency. Default is on.
896 *
897 * @param autoConfig the value of auto config
898 * @return this service dependency
899 */
900 public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
901 ensureNotActive();
902 m_autoConfig = autoConfig;
903 m_autoConfigInvoked = true;
904 return this;
905 }
906
907 /**
908 * Sets auto configuration for this service. Auto configuration allows the
909 * dependency to fill in the attribute in the service implementation that
910 * has the same type and instance name.
911 *
912 * @param instanceName the name of attribute to auto config
913 * @return this service dependency
914 */
915 public synchronized ServiceDependency setAutoConfig(String instanceName) {
916 ensureNotActive();
917 m_autoConfig = (instanceName != null);
918 m_autoConfigInstance = instanceName;
919 m_autoConfigInvoked = true;
920 return this;
921 }
922
923 /**
924 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
925 * dependency is added or removed. When you specify callbacks, the auto configuration
926 * feature is automatically turned off, because we're assuming you don't need it in this
927 * case.
928 *
929 * @param added the method to call when a service was added
930 * @param removed the method to call when a service was removed
931 * @return this service dependency
932 */
933 public synchronized ServiceDependency setCallbacks(String added, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000934 return setCallbacks((Object) null, added, null, removed);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000935 }
936
937 /**
938 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
939 * dependency is added, changed or removed. When you specify callbacks, the auto
940 * configuration feature is automatically turned off, because we're assuming you don't
941 * need it in this case.
942 *
943 * @param added the method to call when a service was added
944 * @param changed the method to call when a service was changed
945 * @param removed the method to call when a service was removed
946 * @return this service dependency
947 */
948 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000949 return setCallbacks((Object) null, added, changed, removed);
950 }
951
952 /**
953 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
954 * dependency is added, changed or removed. When you specify callbacks, the auto
955 * configuration feature is automatically turned off, because we're assuming you don't
956 * need it in this case.
957 * @param added the method to call when a service was added
958 * @param changed the method to call when a service was changed
959 * @param removed the method to call when a service was removed
960 * @param swapped the method to call when the service was swapped due to addition or
961 * removal of an aspect
962 * @return this service dependency
963 */
964 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
965 return setCallbacks((Object) null, added, changed, removed, swapped);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000966 }
967
968 /**
969 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
970 * dependency is added or removed. They are called on the instance you provide. When you
971 * specify callbacks, the auto configuration feature is automatically turned off, because
972 * we're assuming you don't need it in this case.
973 *
974 * @param instance the instance to call the callbacks on
975 * @param added the method to call when a service was added
976 * @param removed the method to call when a service was removed
977 * @return this service dependency
978 */
979 public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000980 return setCallbacks(instance, added, (String) null, removed);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000981 }
982
983 /**
984 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
985 * dependency is added, changed or removed. They are called on the instance you provide. When you
986 * specify callbacks, the auto configuration feature is automatically turned off, because
987 * we're assuming you don't need it in this case.
988 *
989 * @param instance the instance to call the callbacks on
990 * @param added the method to call when a service was added
991 * @param changed the method to call when a service was changed
992 * @param removed the method to call when a service was removed
993 * @return this service dependency
994 */
995 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000996 return setCallbacks(instance, added, changed, removed, null);
997 }
998
999 /**
1000 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
1001 * dependency is added, changed or removed. When you specify callbacks, the auto
1002 * configuration feature is automatically turned off, because we're assuming you don't
1003 * need it in this case.
1004 * @param instance the instance to call the callbacks on
1005 * @param added the method to call when a service was added
1006 * @param changed the method to call when a service was changed
1007 * @param removed the method to call when a service was removed
1008 * @param swapped the method to call when the service was swapped due to addition or
1009 * removal of an aspect
1010 * @return this service dependency
1011 */
1012 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
Marcel Offermansa962bc92009-11-21 17:59:33 +00001013 ensureNotActive();
1014 // if at least one valid callback is specified, we turn off auto configuration, unless
1015 // someone already explicitly invoked autoConfig
Marcel Offermans7f0cec02011-11-04 13:39:28 +00001016 if ((added != null || removed != null || changed != null || swapped != null) && ! m_autoConfigInvoked) {
Marcel Offermansa962bc92009-11-21 17:59:33 +00001017 setAutoConfig(false);
1018 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +00001019 m_callbackInstance = instance;
Marcel Offermansa962bc92009-11-21 17:59:33 +00001020 m_callbackAdded = added;
1021 m_callbackChanged = changed;
Marcel Offermans7f0cec02011-11-04 13:39:28 +00001022 m_callbackRemoved = removed;
1023 m_callbackSwapped = swapped;
1024 return this;
Marcel Offermansa962bc92009-11-21 17:59:33 +00001025 }
1026
1027 private void ensureNotActive() {
1028 if (m_tracker != null) {
1029 throw new IllegalStateException("Cannot modify state while active.");
1030 }
1031 }
1032
1033 public synchronized String toString() {
1034 return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
1035 }
1036
1037 public String getAutoConfigName() {
1038 return m_autoConfigInstance;
1039 }
Marcel Offermans001db052009-12-08 08:58:40 +00001040
1041 public Object getAutoConfigInstance() {
1042 return lookupService();
1043 }
1044
1045 public Class getAutoConfigType() {
1046 return getInterface();
1047 }
Marcel Offermansa962bc92009-11-21 17:59:33 +00001048
1049 public String getName() {
1050 StringBuilder sb = new StringBuilder();
Marcel Offermansb1959f42010-07-01 12:23:51 +00001051 if (m_trackedServiceName != null) {
1052 sb.append(m_trackedServiceName.getName());
1053 if (m_trackedServiceFilterUnmodified != null) {
1054 sb.append(' ');
1055 sb.append(m_trackedServiceFilterUnmodified);
1056 }
1057 }
1058 if (m_trackedServiceReference != null) {
Marcel Offermans9e50ef32010-10-07 11:37:00 +00001059 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
Marcel Offermansa962bc92009-11-21 17:59:33 +00001060 }
1061 return sb.toString();
1062 }
1063
Marcel Offermansa962bc92009-11-21 17:59:33 +00001064 public String getType() {
1065 return "service";
1066 }
Marcel Offermans001db052009-12-08 08:58:40 +00001067
1068 public void invokeAdded(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +00001069 ServiceReference[] refs = m_tracker.getServiceReferences();
1070 if (refs != null) {
1071 for (int i = 0; i < refs.length; i++) {
1072 ServiceReference sr = refs[i];
1073 Object svc = m_context.getService(sr);
1074 invokeAdded(service, sr, svc);
1075 }
1076 }
Marcel Offermans001db052009-12-08 08:58:40 +00001077 }
Marcel Offermansbde9a432010-05-11 08:01:28 +00001078
Marcel Offermans001db052009-12-08 08:58:40 +00001079 public void invokeRemoved(DependencyService service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +00001080 Set references = null;
1081 synchronized (m_sr) {
1082 references = (Set) m_sr.get(service);
1083 }
Marcel Offermans7784e402011-01-21 20:41:50 +00001084 Tuple[] refs = (Tuple[]) (references != null ? references.toArray(new Tuple[references.size()]) : new Tuple[0]);
Marcel Offermans09e14aa2010-11-25 13:36:23 +00001085
1086 for (int i = 0; i < refs.length; i++) {
Marcel Offermans7784e402011-01-21 20:41:50 +00001087 ServiceReference sr = refs[i].getServiceReference();
1088 Object svc = refs[i].getService();
Marcel Offermans09e14aa2010-11-25 13:36:23 +00001089 invokeRemoved(service, sr, svc);
Marcel Offermansbde9a432010-05-11 08:01:28 +00001090 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +00001091 if (references != null) {
1092 references.clear();
1093 }
Marcel Offermans001db052009-12-08 08:58:40 +00001094 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +00001095
1096 public Dictionary getProperties() {
Marcel Offermans26081d32010-07-12 12:43:42 +00001097 ServiceReference reference = lookupServiceReference();
1098 Object service = lookupService();
1099 if (reference != null) {
1100 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
1101 try {
1102 return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
1103 }
1104 catch (InvocationTargetException e) {
1105 m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
1106 }
1107 catch (Exception e) {
1108 m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
1109 }
1110 throw new IllegalStateException("Could not invoke callback");
1111 }
1112 else {
1113 Properties props = new Properties();
1114 String[] keys = reference.getPropertyKeys();
1115 for (int i = 0; i < keys.length; i++) {
1116 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
1117 props.put(keys[i], reference.getProperty(keys[i]));
1118 }
1119 }
1120 return props;
1121 }
1122 }
1123 else {
1124 throw new IllegalStateException("cannot find service reference");
1125 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +00001126 }
1127
1128 public boolean isPropagated() {
Marcel Offermans26081d32010-07-12 12:43:42 +00001129 return m_propagate;
1130 }
1131
1132 public ServiceDependency setPropagate(boolean propagate) {
1133 ensureNotActive();
1134 m_propagate = propagate;
1135 return this;
1136 }
1137
1138 public ServiceDependency setPropagate(Object instance, String method) {
1139 setPropagate(instance != null && method != null);
1140 m_propagateCallbackInstance = instance;
1141 m_propagateCallbackMethod = method;
1142 return this;
Marcel Offermans117aa2f2009-12-10 09:48:17 +00001143 }
Marcel Offermans7f0cec02011-11-04 13:39:28 +00001144
Marcel Offermansa962bc92009-11-21 17:59:33 +00001145}