blob: 3eab51dac60501d8eefc3e7184a0b24a27d9f3a9 [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 Rop1ad67322013-10-25 18:39:51 +000044import org.apache.felix.dm.impl.BlockingSerialExecutor;
Pierre De Ropaacb3aa2009-12-04 22:53:23 +000045import org.apache.felix.dm.impl.DefaultNullObject;
46import org.apache.felix.dm.impl.Logger;
Marcel Offermansfaaed472010-09-08 10:07:32 +000047import org.apache.felix.dm.tracker.ServiceTracker;
48import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
Marcel Offermansa962bc92009-11-21 17:59:33 +000049import org.osgi.framework.BundleContext;
50import org.osgi.framework.Constants;
51import org.osgi.framework.InvalidSyntaxException;
52import org.osgi.framework.ServiceReference;
Marcel Offermans26081d32010-07-12 12:43:42 +000053import org.osgi.service.log.LogService;
Marcel Offermansa962bc92009-11-21 17:59:33 +000054
55/**
56 * Service dependency that can track an OSGi service.
57 *
58 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
59 */
Pierre De Rop1ad67322013-10-25 18:39:51 +000060public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer,
61 ComponentDependencyDeclaration {
62 protected final List m_services = new ArrayList();
Marcel Offermans74363c32009-11-23 19:56:08 +000063 protected volatile ServiceTracker m_tracker;
Pierre De Rop1ad67322013-10-25 18:39:51 +000064 protected final BundleContext m_context;
Marcel Offermans74363c32009-11-23 19:56:08 +000065 protected volatile Class m_trackedServiceName;
Pierre De Rop1ad67322013-10-25 18:39:51 +000066 private volatile Object m_nullObject;
Marcel Offermansa962bc92009-11-21 17:59:33 +000067 private volatile String m_trackedServiceFilter;
68 private volatile String m_trackedServiceFilterUnmodified;
69 private volatile ServiceReference m_trackedServiceReference;
Pierre De Rop1ad67322013-10-25 18:39:51 +000070 private volatile Object m_callbackInstance;
71 private volatile String m_callbackAdded;
72 private volatile String m_callbackChanged;
73 private volatile String m_callbackRemoved;
74 private volatile String m_callbackSwapped;
Marcel Offermansa962bc92009-11-21 17:59:33 +000075 private boolean m_autoConfig;
Pierre De Rop1ad67322013-10-25 18:39:51 +000076 private volatile String m_autoConfigInstance;
Marcel Offermansa962bc92009-11-21 17:59:33 +000077 private boolean m_autoConfigInvoked;
Pierre De Rop1ad67322013-10-25 18:39:51 +000078 private volatile Object m_defaultImplementation;
79 private volatile Object m_defaultImplementationInstance;
Marcel Offermansb196d722009-11-26 17:12:12 +000080 private boolean m_isAvailable;
Pierre De Rop1ad67322013-10-25 18:39:51 +000081 private volatile boolean m_propagate;
82 private volatile Object m_propagateCallbackInstance;
83 private volatile String m_propagateCallbackMethod;
Marcel Offermans7784e402011-01-21 20:41:50 +000084 private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
Pierre De Rop1ad67322013-10-25 18:39:51 +000085 private final Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
86 private volatile boolean m_debug = false;
87 private volatile String m_debugKey = null;
88
89 /**
90 * Executor used to ensure proper synchronization without holding locks.
91 */
92 private final BlockingSerialExecutor _serial = new BlockingSerialExecutor();
93
94 // ----------------------- Inner classes --------------------------------------------------------------
95
Marcel Offermansa962bc92009-11-21 17:59:33 +000096 private static final Comparator COMPARATOR = new Comparator() {
97 public int getRank(ServiceReference ref) {
98 Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
99 if (ranking != null && (ranking instanceof Integer)) {
100 return ((Integer) ranking).intValue();
101 }
102 return 0;
103 }
104
105 public int compare(Object a, Object b) {
106 ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
107 int ranka = getRank(ra);
108 int rankb = getRank(rb);
109 if (ranka < rankb) {
110 return -1;
Pierre De Rop1ad67322013-10-25 18:39:51 +0000111 } else if (ranka > rankb) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000112 return 1;
113 }
114 return 0;
115 }
116 };
Pierre De Rop1ad67322013-10-25 18:39:51 +0000117
118 private static final class Tuple /* <ServiceReference, Object> */{
Marcel Offermans7784e402011-01-21 20:41:50 +0000119 private final ServiceReference m_serviceReference;
120 private final Object m_service;
Pierre De Rop1ad67322013-10-25 18:39:51 +0000121
Marcel Offermans7784e402011-01-21 20:41:50 +0000122 public Tuple(ServiceReference first, Object last) {
123 m_serviceReference = first;
124 m_service = last;
125 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000126
Marcel Offermans7784e402011-01-21 20:41:50 +0000127 public ServiceReference getServiceReference() {
128 return m_serviceReference;
129 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000130
Marcel Offermans7784e402011-01-21 20:41:50 +0000131 public Object getService() {
132 return m_service;
133 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000134
Marcel Offermans7784e402011-01-21 20:41:50 +0000135 public boolean equals(Object obj) {
136 return ((Tuple) obj).getServiceReference().equals(getServiceReference());
137 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000138
Marcel Offermans7784e402011-01-21 20:41:50 +0000139 public int hashCode() {
140 return m_serviceReference.hashCode();
141 }
142 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000143
Marcel Offermansa962bc92009-11-21 17:59:33 +0000144 /**
145 * Entry to wrap service properties behind a Map.
146 */
Marcel Offermans7784e402011-01-21 20:41:50 +0000147 private static final class ServicePropertiesMapEntry implements Map.Entry {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000148 private final String m_key;
149 private Object m_value;
150
151 public ServicePropertiesMapEntry(String key, Object value) {
152 m_key = key;
153 m_value = value;
154 }
155
156 public Object getKey() {
157 return m_key;
158 }
159
160 public Object getValue() {
161 return m_value;
162 }
163
164 public String toString() {
165 return m_key + "=" + m_value;
166 }
167
168 public Object setValue(Object value) {
169 Object oldValue = m_value;
170 m_value = value;
171 return oldValue;
172 }
173
174 public boolean equals(Object o) {
175 if (!(o instanceof Map.Entry)) {
176 return false;
177 }
178 Map.Entry e = (Map.Entry) o;
179 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
180 }
181
182 public int hashCode() {
183 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
184 }
185
186 private static final boolean eq(Object o1, Object o2) {
187 return (o1 == null ? o2 == null : o1.equals(o2));
188 }
189 }
190
191 /**
192 * Wraps service properties behind a Map.
193 */
194 private final static class ServicePropertiesMap extends AbstractMap {
195 private final ServiceReference m_ref;
196
197 public ServicePropertiesMap(ServiceReference ref) {
198 m_ref = ref;
199 }
200
201 public Object get(Object key) {
202 return m_ref.getProperty(key.toString());
203 }
204
205 public int size() {
206 return m_ref.getPropertyKeys().length;
207 }
208
209 public Set entrySet() {
210 Set set = new HashSet();
211 String[] keys = m_ref.getPropertyKeys();
212 for (int i = 0; i < keys.length; i++) {
213 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
214 }
215 return set;
216 }
217 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000218
219 // ----------------------- Public methods -----------------------------------------------------------
220
Marcel Offermansa962bc92009-11-21 17:59:33 +0000221 /**
222 * Creates a new service dependency.
223 *
224 * @param context the bundle context
225 * @param logger the logger
226 */
Pierre De Ropaacb3aa2009-12-04 22:53:23 +0000227 public ServiceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +0000228 super(logger);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000229 m_context = context;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000230 m_autoConfig = true;
231 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000232
Marcel Offermansb1959f42010-07-01 12:23:51 +0000233 /** Copying constructor that clones an existing instance. */
234 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
235 super(prototype);
Marcel Offermanse7baff52010-12-17 08:51:52 +0000236 synchronized (prototype) {
237 m_context = prototype.m_context;
238 m_autoConfig = prototype.m_autoConfig;
239 m_trackedServiceName = prototype.m_trackedServiceName;
240 m_nullObject = prototype.m_nullObject;
241 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
242 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
243 m_trackedServiceReference = prototype.m_trackedServiceReference;
244 m_callbackInstance = prototype.m_callbackInstance;
245 m_callbackAdded = prototype.m_callbackAdded;
246 m_callbackChanged = prototype.m_callbackChanged;
247 m_callbackRemoved = prototype.m_callbackRemoved;
Xander Uiterlinden30303d62012-07-11 14:19:33 +0000248 m_callbackSwapped = prototype.m_callbackSwapped;
Marcel Offermanse7baff52010-12-17 08:51:52 +0000249 m_autoConfigInstance = prototype.m_autoConfigInstance;
250 m_defaultImplementation = prototype.m_defaultImplementation;
251 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000252 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000253
254 //@Override
255 public ServiceDependency setDebug(String identifier) {
256 this.m_debug = true;
257 this.m_debugKey = identifier;
258 return this;
259 }
260
261 //@Override
Marcel Offermansb1959f42010-07-01 12:23:51 +0000262 public Dependency createCopy() {
263 return new ServiceDependencyImpl(this);
264 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000265
266 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000267 public synchronized boolean isAutoConfig() {
268 return m_autoConfig;
269 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000270
271 //@Override
Marcel Offermansb196d722009-11-26 17:12:12 +0000272 public synchronized boolean isAvailable() {
273 return m_isAvailable;
274 }
275
Pierre De Rop1ad67322013-10-25 18:39:51 +0000276 //@Override
277 public void start(final DependencyService service) {
278 _serial.execute(new Runnable() {
279 public void run() {
280 // this code is executed exclusively and without holding any locks
281 _start(service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000282 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000283 });
Marcel Offermansa962bc92009-11-21 17:59:33 +0000284 }
285
Pierre De Rop1ad67322013-10-25 18:39:51 +0000286 //@Override
287 public void stop(final DependencyService service) {
288 _serial.execute(new Runnable() {
289 public void run() {
290 // this code is executed exclusively and without holding any locks
291 _stop(service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000292 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000293 });
Marcel Offermansa962bc92009-11-21 17:59:33 +0000294 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000295
Pierre De Rop1ad67322013-10-25 18:39:51 +0000296 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000297 public Object addingService(ServiceReference ref) {
298 Object service = m_context.getService(ref);
299 // first check to make sure the service is actually an instance of our service
300 if (!m_trackedServiceName.isInstance(service)) {
301 return null;
302 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000303 return service;
304 }
305
Pierre De Rop1ad67322013-10-25 18:39:51 +0000306 //@Override
307 public void addedService(final ServiceReference ref, final Object service) {
308 _serial.execute(new Runnable() {
309 public void run() {
310 // this code is executed exclusively and without holding any locks
311 _addedService(ref, service);
312 }
313 });
314 }
315
316 //@Override
317 public void modifiedService(final ServiceReference ref, final Object service) {
318 _serial.execute(new Runnable() {
319 public void run() {
320 // this code is executed exclusively and without holding any locks
321 _modifiedService(ref, service);
322 }
323 });
324 }
325
326 //@Override
327 public void removedService(final ServiceReference ref, final Object service) {
328 _serial.execute(new Runnable() {
329 public void run() {
330 // this code is executed exclusively and without holding any locks
331 _removedService(ref, service);
332 }
333 });
334 }
335
336 //@Override
337 public void invokeAdded(final DependencyService service) {
338 _serial.execute(new Runnable() {
339 public void run() {
340 // this code is executed exclusively and without holding any locks
341 _invokeAdded(service);
342 }
343 });
344 }
345
346 //@Override
347 public void invokeRemoved(final DependencyService service) {
348 _serial.execute(new Runnable() {
349 public void run() {
350 // this code is executed exclusively and without holding any locks
351 _invokeRemoved(service);
352 }
353 });
354 }
355
356 //@Override
357 public synchronized String toString() {
358 return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
359 }
360
361 //@Override
362 public String getAutoConfigName() {
363 return m_autoConfigInstance;
364 }
365
366 //@Override
367 public Object getAutoConfigInstance() {
368 return lookupService();
369 }
370
371 //@Override
372 public Class getAutoConfigType() {
373 return getInterface();
374 }
375
376 //@Override
377 public String getName() {
378 StringBuilder sb = new StringBuilder();
379 if (m_trackedServiceName != null) {
380 sb.append(m_trackedServiceName.getName());
381 if (m_trackedServiceFilterUnmodified != null) {
382 sb.append(' ');
383 sb.append(m_trackedServiceFilterUnmodified);
384 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000385 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000386 if (m_trackedServiceReference != null) {
387 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID) + "}");
388 }
389 return sb.toString();
390 }
391
392 //@Override
393 public String getType() {
394 return "service";
395 }
396
397 //@Override
398 public Dictionary getProperties() {
399 ServiceReference reference = lookupServiceReference();
400 Object service = lookupService();
401 if (reference != null) {
402 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
403 try {
404 return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
405 new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
406 {reference, service}, {reference}});
407 } catch (InvocationTargetException e) {
408 m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
409 } catch (Exception e) {
410 m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
Marcel Offermansfea14842010-10-14 14:45:36 +0000411 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000412 throw new IllegalStateException("Could not invoke callback");
413 } else {
414 Properties props = new Properties();
415 String[] keys = reference.getPropertyKeys();
416 for (int i = 0; i < keys.length; i++) {
417 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
418 props.put(keys[i], reference.getProperty(keys[i]));
419 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000420 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000421 return props;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000422 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000423 } else {
424 throw new IllegalStateException("cannot find service reference");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000425 }
426 }
427
Pierre De Rop1ad67322013-10-25 18:39:51 +0000428 //@Override
429 public boolean isPropagated() {
430 return m_propagate;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000431 }
432
Marcel Offermansa962bc92009-11-21 17:59:33 +0000433 // ----- CREATION
434
435 /**
436 * Sets the name of the service that should be tracked.
437 *
438 * @param serviceName the name of the service
439 * @return this service dependency
440 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000441 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000442 public synchronized ServiceDependency setService(Class serviceName) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000443 setService(serviceName, null, null);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000444 return this;
445 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000446
Marcel Offermansa962bc92009-11-21 17:59:33 +0000447 /**
448 * Sets the name of the service that should be tracked. You can either specify
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000449 * only the name, only the filter, or the name and a filter.
450 * <p>
451 * If you specify name and filter, the filter is used
Marcel Offermansa962bc92009-11-21 17:59:33 +0000452 * to track the service and should only return services of the type that was specified
453 * in the name. To make sure of this, the filter is actually extended internally to
454 * filter on the correct name.
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000455 * <p>
456 * If you specify only the filter, the name is assumed to be a service of type
457 * <code>Object</code> which means that, when auto configuration is on, instances
458 * of that service will be injected in any field of type <code>Object</code>.
Marcel Offermansa962bc92009-11-21 17:59:33 +0000459 *
460 * @param serviceName the name of the service
461 * @param serviceFilter the filter condition
462 * @return this service dependency
463 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000464 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000465 public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000466 setService(serviceName, null, serviceFilter);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000467 return this;
468 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000469
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000470 /**
471 * Sets the name of the service that should be tracked. The name is assumed to be
472 * a service of type <code>Object</code> which means that, when auto configuration
473 * is on, instances of that service will be injected in any field of type
474 * <code>Object</code>.
475 *
476 * @param serviceFilter the filter condition
477 * @return this service dependency
478 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000479 //@Override
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000480 public synchronized ServiceDependency setService(String serviceFilter) {
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000481 if (serviceFilter == null) {
482 throw new IllegalArgumentException("Service filter cannot be null.");
483 }
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000484 setService(null, null, serviceFilter);
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000485 return this;
486 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000487
488 /**
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000489 * Sets the name of the service that should be tracked. By specifying the service
490 * reference of the service you want to track, you can directly track a single
491 * service. The name you use must match the type of service referred to by the
492 * service reference and it is up to you to make sure that is the case.
Marcel Offermansa962bc92009-11-21 17:59:33 +0000493 *
494 * @param serviceName the name of the service
495 * @param serviceReference the service reference to track
496 * @return this service dependency
497 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000498 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000499 public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
Marcel Offermans8a1fa512011-03-08 15:33:01 +0000500 setService(serviceName, serviceReference, null);
501 return this;
502 }
503
Marcel Offermansa962bc92009-11-21 17:59:33 +0000504 /**
505 * Sets the default implementation for this service dependency. You can use this to supply
506 * your own implementation that will be used instead of a Null Object when the dependency is
507 * not available. This is also convenient if the service dependency is not an interface
508 * (which would cause the Null Object creation to fail) but a class.
509 *
510 * @param implementation the instance to use or the class to instantiate if you want to lazily
511 * instantiate this implementation
512 * @return this service dependency
513 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000514 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000515 public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
516 ensureNotActive();
517 m_defaultImplementation = implementation;
518 return this;
519 }
520
521 /**
522 * Sets the required flag which determines if this service is required or not.
523 *
524 * @param required the required flag
525 * @return this service dependency
526 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000527 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000528 public synchronized ServiceDependency setRequired(boolean required) {
529 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000530 setIsRequired(required);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000531 return this;
532 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000533
534 //@Override
Marcel Offermanse14b3422009-11-25 23:04:32 +0000535 public ServiceDependency setInstanceBound(boolean isInstanceBound) {
Marcel Offermans61a81142010-04-02 15:16:50 +0000536 setIsInstanceBound(isInstanceBound);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000537 return this;
538 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000539
540 /**
541 * Sets auto configuration for this service. Auto configuration allows the
542 * dependency to fill in any attributes in the service implementation that
543 * are of the same type as this dependency. Default is on.
544 *
545 * @param autoConfig the value of auto config
546 * @return this service dependency
547 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000548 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000549 public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
550 ensureNotActive();
551 m_autoConfig = autoConfig;
552 m_autoConfigInvoked = true;
553 return this;
554 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000555
Marcel Offermansa962bc92009-11-21 17:59:33 +0000556 /**
557 * Sets auto configuration for this service. Auto configuration allows the
558 * dependency to fill in the attribute in the service implementation that
559 * has the same type and instance name.
560 *
561 * @param instanceName the name of attribute to auto config
562 * @return this service dependency
563 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000564 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000565 public synchronized ServiceDependency setAutoConfig(String instanceName) {
566 ensureNotActive();
567 m_autoConfig = (instanceName != null);
568 m_autoConfigInstance = instanceName;
569 m_autoConfigInvoked = true;
570 return this;
571 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000572
Marcel Offermansa962bc92009-11-21 17:59:33 +0000573 /**
574 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
575 * dependency is added or removed. When you specify callbacks, the auto configuration
576 * feature is automatically turned off, because we're assuming you don't need it in this
577 * case.
578 *
579 * @param added the method to call when a service was added
580 * @param removed the method to call when a service was removed
581 * @return this service dependency
582 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000583 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000584 public synchronized ServiceDependency setCallbacks(String added, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000585 return setCallbacks((Object) null, added, null, removed);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000586 }
587
588 /**
589 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
590 * dependency is added, changed or removed. When you specify callbacks, the auto
591 * configuration feature is automatically turned off, because we're assuming you don't
592 * need it in this case.
593 *
594 * @param added the method to call when a service was added
595 * @param changed the method to call when a service was changed
596 * @param removed the method to call when a service was removed
597 * @return this service dependency
598 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000599 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000600 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000601 return setCallbacks((Object) null, added, changed, removed);
602 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000603
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000604 /**
605 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
606 * dependency is added, changed or removed. When you specify callbacks, the auto
607 * configuration feature is automatically turned off, because we're assuming you don't
608 * need it in this case.
609 * @param added the method to call when a service was added
610 * @param changed the method to call when a service was changed
611 * @param removed the method to call when a service was removed
612 * @param swapped the method to call when the service was swapped due to addition or
613 * removal of an aspect
614 * @return this service dependency
615 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000616 //@Override
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000617 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
Pierre De Rop1ad67322013-10-25 18:39:51 +0000618 return setCallbacks((Object) null, added, changed, removed, swapped);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000619 }
620
621 /**
622 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
623 * dependency is added or removed. They are called on the instance you provide. When you
624 * specify callbacks, the auto configuration feature is automatically turned off, because
625 * we're assuming you don't need it in this case.
626 *
627 * @param instance the instance to call the callbacks on
628 * @param added the method to call when a service was added
629 * @param removed the method to call when a service was removed
630 * @return this service dependency
631 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000632 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000633 public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000634 return setCallbacks(instance, added, (String) null, removed);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000635 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000636
Marcel Offermansa962bc92009-11-21 17:59:33 +0000637 /**
638 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
639 * dependency is added, changed or removed. They are called on the instance you provide. When you
640 * specify callbacks, the auto configuration feature is automatically turned off, because
641 * we're assuming you don't need it in this case.
642 *
643 * @param instance the instance to call the callbacks on
644 * @param added the method to call when a service was added
645 * @param changed the method to call when a service was changed
646 * @param removed the method to call when a service was removed
647 * @return this service dependency
648 */
Pierre De Rop1ad67322013-10-25 18:39:51 +0000649 //@Override
Marcel Offermansa962bc92009-11-21 17:59:33 +0000650 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
Pierre De Rop1ad67322013-10-25 18:39:51 +0000651 return setCallbacks(instance, added, changed, removed, null);
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000652 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000653
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000654 /**
655 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
656 * dependency is added, changed or removed. When you specify callbacks, the auto
657 * configuration feature is automatically turned off, because we're assuming you don't
658 * need it in this case.
659 * @param instance the instance to call the callbacks on
660 * @param added the method to call when a service was added
661 * @param changed the method to call when a service was changed
662 * @param removed the method to call when a service was removed
663 * @param swapped the method to call when the service was swapped due to addition or
664 * removal of an aspect
665 * @return this service dependency
Pierre De Rop1ad67322013-10-25 18:39:51 +0000666 */
667 //@Override
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000668 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000669 ensureNotActive();
670 // if at least one valid callback is specified, we turn off auto configuration, unless
671 // someone already explicitly invoked autoConfig
Pierre De Rop1ad67322013-10-25 18:39:51 +0000672 if ((added != null || removed != null || changed != null || swapped != null) && !m_autoConfigInvoked) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000673 setAutoConfig(false);
674 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000675 m_callbackInstance = instance;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000676 m_callbackAdded = added;
677 m_callbackChanged = changed;
Pierre De Rop1ad67322013-10-25 18:39:51 +0000678 m_callbackRemoved = removed;
Marcel Offermans7f0cec02011-11-04 13:39:28 +0000679 m_callbackSwapped = swapped;
Pierre De Rop1ad67322013-10-25 18:39:51 +0000680 return this;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000681 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000682
683 //@Override
684 public ServiceDependency setPropagate(boolean propagate) {
685 ensureNotActive();
686 m_propagate = propagate;
687 return this;
688 }
689
690 //@Override
691 public ServiceDependency setPropagate(Object instance, String method) {
692 setPropagate(instance != null && method != null);
693 m_propagateCallbackInstance = instance;
694 m_propagateCallbackMethod = method;
695 return this;
696 }
697
698 // --------------------------------------- Protected methods -------------------------------------------------------------------
699
700 protected synchronized boolean makeAvailable() {
701 if (!isAvailable()) {
702 m_isAvailable = true;
703 return true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000704 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000705 return false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000706 }
707
Pierre De Rop1ad67322013-10-25 18:39:51 +0000708 protected synchronized Object getService() {
709 Object service = null;
710 if (m_isStarted) {
711 service = m_tracker.getService();
712 }
713 if (service == null && isAutoConfig()) {
714 service = getDefaultImplementation();
715 if (service == null) {
716 service = getNullObject();
Marcel Offermansb1959f42010-07-01 12:23:51 +0000717 }
718 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000719 return service;
720 }
721
722 // --------------------------------------- Private methods --------------------------------------------
723
724 private void _start(DependencyService service) {
725 boolean needsStarting = false;
726 synchronized (this) {
727 m_services.add(service);
728 if (!m_isStarted) {
729 if (m_trackedServiceName != null) {
730 if (m_trackedServiceFilter != null) {
731 try {
732 m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
733 } catch (InvalidSyntaxException e) {
734 throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
735 }
736 } else if (m_trackedServiceReference != null) {
737 m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
738 } else {
739 m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
740 }
741 } else {
742 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
743 }
744 m_isStarted = true;
745 needsStarting = true;
746 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000747 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000748 if (needsStarting) {
749 // when the swapped callback is set, also track the aspects
750 boolean trackAllServices = false;
751 boolean trackAllAspects = false;
752 if (m_callbackSwapped != null) {
753 trackAllAspects = true;
754 }
755 m_tracker.open(trackAllServices, trackAllAspects);
756 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000757 }
758
Pierre De Rop1ad67322013-10-25 18:39:51 +0000759 private void _stop(DependencyService service) {
760 boolean needsStopping = false;
761 synchronized (this) {
762 if (m_services.size() == 1 && m_services.contains(service)) {
763 m_isStarted = false;
764 needsStopping = true;
765 }
766 }
767 if (needsStopping) {
768 m_tracker.close();
769 m_tracker = null;
770 }
771 //moved this down
772 synchronized (this) {
773 m_services.remove(service);
774 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000775 }
Marcel Offermans001db052009-12-08 08:58:40 +0000776
Pierre De Rop1ad67322013-10-25 18:39:51 +0000777 private void _addedService(ServiceReference ref, Object service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000778 if (m_debug) {
779 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] addedservice: " + ref);
780 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000781 boolean makeAvailable = makeAvailable();
782
783 Object[] services;
784 synchronized (this) {
785 services = m_services.toArray();
786 }
787 for (int i = 0; i < services.length; i++) {
788 DependencyService ds = (DependencyService) services[i];
789 if (makeAvailable) {
790 if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000791 if (m_debug) {
792 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
793 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000794 invokeAdded(ds, ref, service); //**
795 }
796 // The dependency callback will be deferred until all required dependency are available.
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000797 if (m_debug) {
798 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] dependency available: " + ref);
799 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000800 ds.dependencyAvailable(this); //**
801 if (!isRequired()) {
802 // For optional dependency, we always invoke callback, because at this point, we know
803 // that the service has been started, and the service start method has been called.
804 // (See the ServiceImpl.bindService method, which will activate optional dependencies using
805 // startTrackingOptional() method).
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000806 if (m_debug) {
807 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
808 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000809 invokeAdded(ds, ref, service); //**
810 }
811 } else {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000812 if (m_debug) {
813 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] dependency changed: " + ref);
814 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000815 ds.dependencyChanged(this); //**
816 // At this point, either the dependency is optional (meaning that the service has been started,
817 // because if not, then our dependency would not be active); or the dependency is required,
818 // meaning that either the service is not yet started, or already started.
819 // In all cases, we have to inject the required dependency.
820
821 // we only try to invoke the method here if we are really already instantiated
822 if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000823 if (m_debug) {
824 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + ref);
825 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000826 invokeAdded(ds, ref, service); //**
827 }
828 }
829 }
830 }
831
832 private void _modifiedService(ServiceReference ref, Object service) {
833 Object[] services;
834 synchronized (this) {
835 services = m_services.toArray();
836 }
837 for (int i = 0; i < services.length; i++) {
838 DependencyService ds = (DependencyService) services[i];
839 ds.dependencyChanged(this);
840 if (ds.isRegistered()) {
841 invokeChanged(ds, ref, service);
842 }
843 }
844 }
845
846 private void _removedService(ServiceReference ref, Object service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000847 if (m_debug) {
848 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] removedservice: " + ref + ", rank: " + ref.getProperty("service.ranking"));
849 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000850 boolean makeUnavailable = makeUnavailable();
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000851 if (m_debug) {
852 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] make unavailable: " + makeUnavailable);
853 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000854 Object[] services;
855 synchronized (this) {
856 services = m_services.toArray();
857 }
858
859 for (int i = 0; i < services.length; i++) {
860 DependencyService ds = (DependencyService) services[i];
861 if (makeUnavailable) {
862 ds.dependencyUnavailable(this);
863 // when the dependency is optional or the dependency is instance bound and the component is instantiated (and the dependency is required)
864 // then remove is invoked. In other cases the removed has been when the component was unconfigured.
865 if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
866 invokeRemoved(ds, ref, service);
867 }
868 } else {
869 ds.dependencyChanged(this);
870 invokeRemoved(ds, ref, service);
871 }
872 }
873 // unget what we got in addingService (see ServiceTracker 701.4.1)
874 m_context.ungetService(ref);
875
876 }
877
878 private void _invokeAdded(DependencyService service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000879 if (m_debug) {
880 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added due to configure. (component is activated)");
881 }
Marcel Offermansbde9a432010-05-11 08:01:28 +0000882 ServiceReference[] refs = m_tracker.getServiceReferences();
883 if (refs != null) {
884 for (int i = 0; i < refs.length; i++) {
885 ServiceReference sr = refs[i];
886 Object svc = m_context.getService(sr);
887 invokeAdded(service, sr, svc);
888 }
889 }
Marcel Offermans001db052009-12-08 08:58:40 +0000890 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000891
892 private void _invokeRemoved(DependencyService service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +0000893 if (m_debug) {
894 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke removed due to unconfigure. (component is destroyed)");
895 }
Marcel Offermanse7baff52010-12-17 08:51:52 +0000896 Set references = null;
Xander Uiterlinden48f267b2013-06-03 12:13:09 +0000897 Object[] tupleArray = null;
Marcel Offermanse7baff52010-12-17 08:51:52 +0000898 synchronized (m_sr) {
899 references = (Set) m_sr.get(service);
Xander Uiterlinden48f267b2013-06-03 12:13:09 +0000900 // is this null check necessary ??
901 if (references != null) {
Pierre De Rop1ad67322013-10-25 18:39:51 +0000902 tupleArray = references.toArray(new Tuple[references.size()]);
Xander Uiterlinden48f267b2013-06-03 12:13:09 +0000903 }
Marcel Offermanse7baff52010-12-17 08:51:52 +0000904 }
Xander Uiterlinden48f267b2013-06-03 12:13:09 +0000905
Pierre De Rop1ad67322013-10-25 18:39:51 +0000906 Tuple[] refs = (Tuple[]) (tupleArray != null ? tupleArray : new Tuple[0]);
907
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000908 for (int i = 0; i < refs.length; i++) {
Marcel Offermans7784e402011-01-21 20:41:50 +0000909 ServiceReference sr = refs[i].getServiceReference();
910 Object svc = refs[i].getService();
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000911 invokeRemoved(service, sr, svc);
Marcel Offermansbde9a432010-05-11 08:01:28 +0000912 }
Marcel Offermans001db052009-12-08 08:58:40 +0000913 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000914
Pierre De Rop1ad67322013-10-25 18:39:51 +0000915 private Object lookupService() {
916 Object service = null;
917 if (m_isStarted) {
918 service = getService();
919 } else {
920 ServiceReference[] refs = null;
921 ServiceReference ref = null;
922 if (m_trackedServiceName != null) {
923 if (m_trackedServiceFilter != null) {
924 try {
925 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
926 if (refs != null) {
927 Arrays.sort(refs, COMPARATOR);
928 ref = refs[0];
929 }
930 } catch (InvalidSyntaxException e) {
931 throw new IllegalStateException("Invalid filter definition for dependency.");
Marcel Offermans26081d32010-07-12 12:43:42 +0000932 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000933 } else if (m_trackedServiceReference != null) {
934 ref = m_trackedServiceReference;
935 } else {
936 ref = m_context.getServiceReference(m_trackedServiceName.getName());
Marcel Offermans26081d32010-07-12 12:43:42 +0000937 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000938 if (ref != null) {
939 service = m_context.getService(ref);
940 }
941 } else {
942 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
Marcel Offermans26081d32010-07-12 12:43:42 +0000943 }
944 }
Pierre De Rop1ad67322013-10-25 18:39:51 +0000945 if (service == null && isAutoConfig()) {
946 service = getDefaultImplementation();
947 if (service == null) {
948 service = getNullObject();
949 }
950 }
951 return service;
952 }
953
954 private ServiceReference lookupServiceReference() {
955 // TODO lots of duplication in lookupService()
956 ServiceReference service = null;
957 if (m_isStarted) {
958 service = m_tracker.getServiceReference();
959 } else {
960 ServiceReference[] refs = null;
961 ServiceReference ref = null;
962 if (m_trackedServiceName != null) {
963 if (m_trackedServiceFilter != null) {
964 try {
965 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
966 if (refs != null) {
967 Arrays.sort(refs, COMPARATOR);
968 ref = refs[0];
969 }
970 } catch (InvalidSyntaxException e) {
971 throw new IllegalStateException("Invalid filter definition for dependency.");
972 }
973 } else if (m_trackedServiceReference != null) {
974 ref = m_trackedServiceReference;
975 } else {
976 ref = m_context.getServiceReference(m_trackedServiceName.getName());
977 }
978 if (ref != null) {
979 service = ref;
980 }
981 } else {
982 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
983 }
984 }
985 return service;
986 }
987
988 private synchronized Class getInterface() {
989 return m_trackedServiceName;
990 }
991
992 private Object getNullObject() {
993 if (m_nullObject == null) {
994 Class trackedServiceName;
995 synchronized (this) {
996 trackedServiceName = m_trackedServiceName;
997 }
998 try {
999 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[]{trackedServiceName},
1000 new DefaultNullObject());
1001 } catch (Exception e) {
1002 m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
1003 }
1004 }
1005 return m_nullObject;
1006 }
1007
1008 private Object getDefaultImplementation() {
1009 if (m_defaultImplementation != null) {
1010 if (m_defaultImplementation instanceof Class) {
1011 try {
1012 m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
1013 } catch (Exception e) {
1014 m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation
1015 + ".", e);
1016 }
1017 } else {
1018 m_defaultImplementationInstance = m_defaultImplementation;
1019 }
1020 }
1021 return m_defaultImplementationInstance;
1022 }
1023
1024 private void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
1025 // We are already serialized.
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001026 if (m_debug) {
1027 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added");
1028 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001029 boolean added = false;
1030 synchronized (m_sr) {
1031 Set set = (Set) m_sr.get(dependencyService);
1032 if (set == null) {
1033 set = new HashSet();
1034 m_sr.put(dependencyService, set);
1035 }
1036 added = set.add(new Tuple(reference, service));
1037 }
1038 if (added) {
1039 // when a changed callback is specified we might not call the added callback just yet
1040 if (m_callbackSwapped != null) {
1041 handleAspectAwareAdded(dependencyService, reference, service);
1042 } else {
1043 invoke(dependencyService, reference, service, m_callbackAdded);
1044 }
Marcel Offermans26081d32010-07-12 12:43:42 +00001045 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +00001046 }
1047
Pierre De Rop1ad67322013-10-25 18:39:51 +00001048 private void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
1049 invoke(dependencyService, reference, service, m_callbackChanged);
1050 }
1051
1052 private void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001053 if (m_debug) {
1054 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke removed");
1055 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001056 boolean removed = false;
1057 synchronized (m_sr) {
1058 Set set = (Set) m_sr.get(dependencyService);
1059 removed = (set != null && set.remove(new Tuple(reference, service)));
1060 }
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001061 if (m_debug) {
1062 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] removed: " + removed);
1063 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001064 if (removed) {
1065 if (m_callbackSwapped != null) {
1066 handleAspectAwareRemoved(dependencyService, reference, service);
1067 } else {
1068 invoke(dependencyService, reference, service, m_callbackRemoved);
1069 }
1070 }
1071 }
1072
1073 private void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001074 if (m_debug) {
1075 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke: " + name);
1076 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001077 if (name != null) {
1078 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name, new Class[][]{
1079 {Component.class, ServiceReference.class, m_trackedServiceName},
1080 {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class},
1081 {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class},
1082 {Component.class, Map.class, m_trackedServiceName}, {ServiceReference.class, m_trackedServiceName},
1083 {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {},
1084 {Map.class, m_trackedServiceName}}, new Object[][]{{dependencyService, reference, service},
1085 {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service},
1086 {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
1087 {reference, service}, {reference, service}, {reference}, {service}, {service}, {},
1088 {new ServicePropertiesMap(reference), service}});
1089 }
1090 }
1091
1092 private void handleAspectAwareAdded(final DependencyService dependencyService, final ServiceReference reference, final Object service) {
1093 // At this point, we are already serialized: no need to synchronized.
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001094 if (m_debug) {
1095 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] aspectawareadded: " + reference.getProperty("service.ranking"));
1096 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001097 if (componentIsDependencyManagerFactory(dependencyService)) {
1098 // component is either aspect or adapter factory instance, these must be ignored.
1099 return;
1100 }
1101 boolean invokeAdded = false;
1102 boolean invokeSwapped = false;
1103 Integer ranking = ServiceUtil.getRankingAsInteger(reference);
1104 Tuple newHighestRankedService = null;
1105 Tuple prevHighestRankedService = null;
1106 Map rankings = null;
1107 //synchronized (m_componentByRank) { // don't synchronize: we are serialized and we can now invoke callbacks
1108 Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
1109 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
1110 if (componentMap == null) {
1111 // create new componentMap
1112 componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
1113 m_componentByRank.put(dependencyService, componentMap);
1114 }
1115 rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
1116 if (rankings == null) {
1117 // new component added
1118 rankings = new HashMap(); /* <Integer, Tuple> */
1119 componentMap.put(originalServiceId, rankings);
1120 rankings.put(ranking, new Tuple(reference, service));
1121 invokeAdded = true;
1122 }
1123
1124 if (!invokeAdded) {
1125 // current highest ranked
1126 prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
1127 newHighestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001128 if (m_debug) {
1129 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] prevhigh: " + prevHighestRankedService.getServiceReference().getProperty("service.ranking") + ", new high: " +
Pierre De Rop1ad67322013-10-25 18:39:51 +00001130 newHighestRankedService.getServiceReference().getProperty("service.ranking"));
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001131 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001132 if (!prevHighestRankedService.getServiceReference().equals(newHighestRankedService.getServiceReference())) {
1133 // new highest ranked service
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001134 if (m_debug) {
1135 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] New highest ranked to swap to");
1136 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001137 invokeSwapped = true;
1138 } else {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001139 if (m_debug) {
1140 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] Ignoring lower ranked or irrelevant swap");
1141 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001142 }
1143 }
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001144 if (m_debug) {
1145 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] " + m_componentByRank.toString());
1146 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001147
1148 // up until this point should be synchronized on m_componentsByRank to keep integrity of the administration and consequences
1149 // then the do phase comes, here we want to guarantee the effects of this operation are done like they were synchronized, however
1150 // synchronization on m_componentsByRank to too course grained here, so we'd like to switch to synchronization on the
1151 // original service id, therefore we're using our own guarded block to ensure the correct order.
1152
1153 if (invokeAdded) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001154 if (m_debug) {
1155 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke added: " + reference.getProperty("service.ranking"));
1156 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001157 // We can safely invoke callback since we are already serialized.
1158 invoke(dependencyService, reference, service, m_callbackAdded);
1159 } else if (invokeSwapped) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001160 if (m_debug) {
1161 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " +
1162 prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
1163 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001164 // We can safely invoke callback since we are already serialized.
1165 invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(),
1166 newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
1167 }
1168 //}
1169 }
Marcel Offermans26081d32010-07-12 12:43:42 +00001170
Pierre De Rop1ad67322013-10-25 18:39:51 +00001171 private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
1172 Object component = dependencyService.getService();
1173 if (component != null) {
1174 String className = component.getClass().getName();
1175 return className.startsWith("org.apache.felix.dm")
1176 && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
1177 && !className.startsWith("org.apache.felix.dm.test");
1178 }
1179 return false;
1180 }
1181
1182 private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference,
1183 Object newService, Integer newRanking) {
1184 // does a component with a higher ranking exist
1185 synchronized (m_componentByRank) {
1186 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
1187 Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
1188 rankings.put(newRanking, new Tuple(newReference, newService));
1189 Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
1190 return (Tuple) highestEntry.getValue();
1191 }
1192 }
1193
1194 private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
1195 Entry highestEntry = null; /* <Integer, Tuple> */
1196 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
1197 Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
1198 if (rankings != null) {
1199 for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) { /* <Integer, Tuple> */
1200 Entry mapEntry = (Entry) entryIterator.next();
1201 if (highestEntry == null) {
1202 highestEntry = mapEntry;
1203 } else {
1204 if (((Integer) mapEntry.getKey()).intValue() > ((Integer) highestEntry.getKey()).intValue()) {
1205 highestEntry = mapEntry;
1206 }
1207 }
1208 }
1209 }
1210 return highestEntry;
1211 }
1212
1213 private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
1214 // get the collection of rankings
1215 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
1216
1217 Map rankings = null; /* <Integer, Tuple> */
1218 if (componentMap != null) {
1219 rankings = (Map) componentMap.get(serviceId);
1220 }
1221 // if there is only one element left in the collection of rankings
1222 // and this last element has the same ranking as the supplied service (in other words, it is the same)
1223 // then this is the last service
1224 // NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
1225 // because an aspect on top of the original service is being removed (but the original service is still
1226 // there). That in turn triggers:
1227 // 1) a call to added(original-service)
1228 // 2) that causes a swap
1229 // 3) a call to removed(aspect-service) <-- that's what we're talking about
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001230 if (m_debug) {
1231 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] last service: " + m_componentByRank.toString());
1232 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001233 return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry) rankings.entrySet().iterator().next())
1234 .getKey().equals(ServiceUtil.getRankingAsInteger(reference)));
1235 }
1236
1237 private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001238 if (m_debug) {
1239 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] aspectawareremoved: " + reference.getProperty("service.ranking"));
1240 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001241 if (componentIsDependencyManagerFactory(dependencyService)) {
1242 // component is either aspect or adapter factory instance, these must be ignored.
1243 return;
1244 }
1245 // we might need to swap here too!
1246 boolean invokeRemoved = false;
1247 Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
1248 Tuple prevHighestRankedService = null;
1249 Tuple newHighestRankedService = null;
1250 boolean invokeSwapped = false;
1251 Map rankings = null;
1252 // synchronized (m_componentByRank) { // don't synchronize: we are serialized and we invoke callbacks
1253 Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
1254 if (isLastService(dependencyService, reference, service, serviceId)) {
1255 invokeRemoved = true;
1256 } else {
1257 // not the last service, but should we swap?
1258 prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
1259 if (prevHighestRankedService.getServiceReference().equals(reference)) {
1260 // swapping out
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001261 if (m_debug) {
1262 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] Swap out on remove!");
1263 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001264 invokeSwapped = true;
1265 }
1266 }
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001267 if (m_debug) {
1268 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] is last service: " + invokeRemoved);
1269 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001270 // cleanup
1271 Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
1272 if (componentMap != null) {
1273 rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
1274 List rankingsToRemove = new ArrayList();
1275 for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) {
1276 Entry mapEntry = (Entry) entryIterator.next();
1277 if (((Tuple) mapEntry.getValue()).getServiceReference().equals(reference)) {
1278 // remove the reference
1279 // rankings.remove(mapEntry.getKey());
1280 rankingsToRemove.add(mapEntry.getKey());
1281 }
1282 }
1283 for (Iterator rankingIterator = rankingsToRemove.iterator(); rankingIterator.hasNext();) {
1284 rankings.remove(rankingIterator.next());
1285 }
1286 if (rankings.size() == 0) {
1287 componentMap.remove(originalServiceId);
1288 }
1289 if (componentMap.size() == 0) {
1290 m_componentByRank.remove(dependencyService);
1291 }
1292 }
1293 // determine current highest ranked service
1294 if (invokeSwapped) {
1295 newHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
1296 }
1297 if (invokeRemoved) {
1298 // handle invoke outside the sync block since we won't know what will happen there
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001299 if (m_debug) {
1300 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke removed: " + reference.getProperty("service.ranking"));
1301 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001302 // We can safely invoke callback, since we are already serialized
1303 invoke(dependencyService, reference, service, m_callbackRemoved);
1304 } else if (invokeSwapped) {
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001305 if (m_debug) {
1306 m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " +
Pierre De Rop1ad67322013-10-25 18:39:51 +00001307 prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
Xander Uiterlinden71ba6da2013-11-06 13:06:51 +00001308 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001309 // We can safely invoke callback, since we are already serialized
1310 invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(),
1311 newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
1312 }
1313 //}
1314 }
1315
1316 private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous,
1317 ServiceReference currentServiceReference, Object current) {
1318 // sanity check on the service references
1319 Integer oldRank = (Integer) previousReference.getProperty(Constants.SERVICE_RANKING);
1320 Integer newRank = (Integer) currentServiceReference.getProperty(Constants.SERVICE_RANKING);
1321
1322 if (oldRank != null && newRank != null && oldRank.equals(newRank)) {
1323 throw new IllegalStateException("Attempt to swap a service for a service with the same rank! previousReference: "
1324 + previousReference + ", currentReference: " + currentServiceReference);
1325 }
1326
1327 component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][]{
1328 {m_trackedServiceName, m_trackedServiceName}, {Object.class, Object.class},
1329 {ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
1330 {ServiceReference.class, Object.class, ServiceReference.class, Object.class},
1331 {Component.class, m_trackedServiceName, m_trackedServiceName}, {Component.class, Object.class, Object.class},
1332 {Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
1333 {Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class}}, new Object[][]{
1334 {previous, current}, {previous, current}, {previousReference, previous, currentServiceReference, current},
1335 {previousReference, previous, currentServiceReference, current}, {component, previous, current},
1336 {component, previous, current}, {component, previousReference, previous, currentServiceReference, current},
1337 {component, previousReference, previous, currentServiceReference, current}});
1338 }
1339
1340 private synchronized boolean makeUnavailable() {
1341 // TODO should we check also m_isStarted ?
1342 if ((isAvailable()) && (m_isStarted == false || !m_tracker.hasReference())) {
1343 m_isAvailable = false;
1344 return true;
1345 }
1346 return false;
1347 }
1348
1349 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
1350 if (m_callbackInstance == null) {
1351 return dependencyService.getCompositionInstances();
1352 } else {
1353 return new Object[]{m_callbackInstance};
1354 }
1355 }
1356
1357 // ----- CREATION
1358
1359 /** Internal method to set the name, service reference and/or filter. */
1360 private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
Marcel Offermans26081d32010-07-12 12:43:42 +00001361 ensureNotActive();
Pierre De Rop1ad67322013-10-25 18:39:51 +00001362 if (serviceName == null) {
1363 m_trackedServiceName = Object.class;
1364 } else {
1365 m_trackedServiceName = serviceName;
1366 }
1367 if (serviceFilter != null) {
1368 m_trackedServiceFilterUnmodified = serviceFilter;
1369 if (serviceName == null) {
1370 m_trackedServiceFilter = serviceFilter;
1371 } else {
1372 m_trackedServiceFilter = "(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
1373 }
1374 } else {
1375 m_trackedServiceFilterUnmodified = null;
1376 m_trackedServiceFilter = null;
1377 }
1378 if (serviceReference != null) {
1379 m_trackedServiceReference = serviceReference;
1380 if (serviceFilter != null) {
1381 throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
1382 }
1383 } else {
1384 m_trackedServiceReference = null;
1385 }
Marcel Offermans26081d32010-07-12 12:43:42 +00001386 }
Pierre De Rop1ad67322013-10-25 18:39:51 +00001387
1388 private void ensureNotActive() {
1389 if (m_tracker != null) {
1390 throw new IllegalStateException("Cannot modify state while active.");
1391 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +00001392 }
Marcel Offermansa962bc92009-11-21 17:59:33 +00001393}