blob: a248cda45e5d9035e7fc41947a014885380cd92d [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 Offermansa962bc92009-11-21 17:59:33 +000028import java.util.HashSet;
Marcel Offermanse14b3422009-11-25 23:04:32 +000029import java.util.List;
Marcel Offermansa962bc92009-11-21 17:59:33 +000030import java.util.Map;
Marcel Offermans26081d32010-07-12 12:43:42 +000031import java.util.Properties;
Marcel Offermansa962bc92009-11-21 17:59:33 +000032import java.util.Set;
33
Marcel Offermans9e50ef32010-10-07 11:37:00 +000034import org.apache.felix.dm.ComponentDependencyDeclaration;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000035import org.apache.felix.dm.Dependency;
Marcel Offermans3d921212010-08-09 13:37:02 +000036import org.apache.felix.dm.DependencyService;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000037import org.apache.felix.dm.ServiceDependency;
Pierre De Ropaacb3aa2009-12-04 22:53:23 +000038import org.apache.felix.dm.impl.DefaultNullObject;
Marcel Offermans26081d32010-07-12 12:43:42 +000039import org.apache.felix.dm.impl.InvocationUtil;
Pierre De Ropaacb3aa2009-12-04 22:53:23 +000040import org.apache.felix.dm.impl.Logger;
Marcel Offermansfaaed472010-09-08 10:07:32 +000041import org.apache.felix.dm.tracker.ServiceTracker;
42import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
Marcel Offermansa962bc92009-11-21 17:59:33 +000043import org.osgi.framework.BundleContext;
44import org.osgi.framework.Constants;
45import org.osgi.framework.InvalidSyntaxException;
46import org.osgi.framework.ServiceReference;
Marcel Offermans26081d32010-07-12 12:43:42 +000047import org.osgi.service.log.LogService;
Marcel Offermansa962bc92009-11-21 17:59:33 +000048
49/**
50 * Service dependency that can track an OSGi service.
51 *
52 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
53 */
Marcel Offermansfaaed472010-09-08 10:07:32 +000054public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
Marcel Offermanse14b3422009-11-25 23:04:32 +000055 protected List m_services = new ArrayList();
Marcel Offermans74363c32009-11-23 19:56:08 +000056 protected volatile ServiceTracker m_tracker;
57 protected BundleContext m_context;
Marcel Offermans74363c32009-11-23 19:56:08 +000058 protected volatile Class m_trackedServiceName;
Marcel Offermansa962bc92009-11-21 17:59:33 +000059 private Object m_nullObject;
60 private volatile String m_trackedServiceFilter;
61 private volatile String m_trackedServiceFilterUnmodified;
62 private volatile ServiceReference m_trackedServiceReference;
63 private volatile boolean m_isStarted;
64 private Object m_callbackInstance;
65 private String m_callbackAdded;
66 private String m_callbackChanged;
67 private String m_callbackRemoved;
68 private boolean m_autoConfig;
Marcel Offermans74363c32009-11-23 19:56:08 +000069 protected ServiceReference m_reference;
70 protected Object m_serviceInstance;
Marcel Offermansa962bc92009-11-21 17:59:33 +000071 private String m_autoConfigInstance;
72 private boolean m_autoConfigInvoked;
73 private Object m_defaultImplementation;
74 private Object m_defaultImplementationInstance;
Marcel Offermansb196d722009-11-26 17:12:12 +000075 private boolean m_isAvailable;
Marcel Offermansbde9a432010-05-11 08:01:28 +000076 private ServiceReference[] m_references;
Marcel Offermans26081d32010-07-12 12:43:42 +000077 private boolean m_propagate;
78 private Object m_propagateCallbackInstance;
79 private String m_propagateCallbackMethod;
Marcel Offermansa962bc92009-11-21 17:59:33 +000080
81 private static final Comparator COMPARATOR = new Comparator() {
82 public int getRank(ServiceReference ref) {
83 Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
84 if (ranking != null && (ranking instanceof Integer)) {
85 return ((Integer) ranking).intValue();
86 }
87 return 0;
88 }
89
90 public int compare(Object a, Object b) {
91 ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
92 int ranka = getRank(ra);
93 int rankb = getRank(rb);
94 if (ranka < rankb) {
95 return -1;
96 }
97 else if (ranka > rankb) {
98 return 1;
99 }
100 return 0;
101 }
102 };
103
104 /**
105 * Entry to wrap service properties behind a Map.
106 */
107 private final static class ServicePropertiesMapEntry implements Map.Entry {
108 private final String m_key;
109 private Object m_value;
110
111 public ServicePropertiesMapEntry(String key, Object value) {
112 m_key = key;
113 m_value = value;
114 }
115
116 public Object getKey() {
117 return m_key;
118 }
119
120 public Object getValue() {
121 return m_value;
122 }
123
124 public String toString() {
125 return m_key + "=" + m_value;
126 }
127
128 public Object setValue(Object value) {
129 Object oldValue = m_value;
130 m_value = value;
131 return oldValue;
132 }
133
134 public boolean equals(Object o) {
135 if (!(o instanceof Map.Entry)) {
136 return false;
137 }
138 Map.Entry e = (Map.Entry) o;
139 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
140 }
141
142 public int hashCode() {
143 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
144 }
145
146 private static final boolean eq(Object o1, Object o2) {
147 return (o1 == null ? o2 == null : o1.equals(o2));
148 }
149 }
150
151 /**
152 * Wraps service properties behind a Map.
153 */
154 private final static class ServicePropertiesMap extends AbstractMap {
155 private final ServiceReference m_ref;
156
157 public ServicePropertiesMap(ServiceReference ref) {
158 m_ref = ref;
159 }
160
161 public Object get(Object key) {
162 return m_ref.getProperty(key.toString());
163 }
164
165 public int size() {
166 return m_ref.getPropertyKeys().length;
167 }
168
169 public Set entrySet() {
170 Set set = new HashSet();
171 String[] keys = m_ref.getPropertyKeys();
172 for (int i = 0; i < keys.length; i++) {
173 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
174 }
175 return set;
176 }
177 }
178
179 /**
180 * Creates a new service dependency.
181 *
182 * @param context the bundle context
183 * @param logger the logger
184 */
Pierre De Ropaacb3aa2009-12-04 22:53:23 +0000185 public ServiceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +0000186 super(logger);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000187 m_context = context;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000188 m_autoConfig = true;
189 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000190
Marcel Offermansb1959f42010-07-01 12:23:51 +0000191 /** Copying constructor that clones an existing instance. */
192 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
193 super(prototype);
194 m_context = prototype.m_context;
195 m_autoConfig = prototype.m_autoConfig;
196 m_trackedServiceName = prototype.m_trackedServiceName;
197 m_nullObject = prototype.m_nullObject;
198 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
199 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
200 m_trackedServiceReference = prototype.m_trackedServiceReference;
201 m_callbackInstance = prototype.m_callbackInstance;
202 m_callbackAdded = prototype.m_callbackAdded;
203 m_callbackChanged = prototype.m_callbackChanged;
204 m_callbackRemoved = prototype.m_callbackRemoved;
205 m_autoConfigInstance = prototype.m_autoConfigInstance;
206 m_defaultImplementation = prototype.m_defaultImplementation;
207 }
208
209 public Dependency createCopy() {
210 return new ServiceDependencyImpl(this);
211 }
212
Marcel Offermansa962bc92009-11-21 17:59:33 +0000213 public synchronized boolean isAutoConfig() {
214 return m_autoConfig;
215 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000216
Marcel Offermansb196d722009-11-26 17:12:12 +0000217 public synchronized boolean isAvailable() {
218 return m_isAvailable;
219 }
220
Marcel Offermansa962bc92009-11-21 17:59:33 +0000221 public synchronized Object getService() {
222 Object service = null;
223 if (m_isStarted) {
224 service = m_tracker.getService();
225 }
Marcel Offermans001db052009-12-08 08:58:40 +0000226 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000227 service = getDefaultImplementation();
228 if (service == null) {
229 service = getNullObject();
230 }
231 }
232 return service;
233 }
234
235 public Object lookupService() {
236 Object service = null;
237 if (m_isStarted) {
Marcel Offermans001db052009-12-08 08:58:40 +0000238 service = getService();
Marcel Offermansa962bc92009-11-21 17:59:33 +0000239 }
240 else {
241 ServiceReference[] refs = null;
242 ServiceReference ref = null;
243 if (m_trackedServiceName != null) {
244 if (m_trackedServiceFilter != null) {
245 try {
246 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
247 if (refs != null) {
248 Arrays.sort(refs, COMPARATOR);
249 ref = refs[0];
250 }
251 }
252 catch (InvalidSyntaxException e) {
253 throw new IllegalStateException("Invalid filter definition for dependency.");
254 }
255 }
256 else if (m_trackedServiceReference != null) {
257 ref = m_trackedServiceReference;
258 }
259 else {
260 ref = m_context.getServiceReference(m_trackedServiceName.getName());
261 }
262 if (ref != null) {
263 service = m_context.getService(ref);
264 }
265 }
266 else {
267 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
268 }
269 }
Marcel Offermans001db052009-12-08 08:58:40 +0000270 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000271 service = getDefaultImplementation();
272 if (service == null) {
273 service = getNullObject();
274 }
275 }
276 return service;
277 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000278
Marcel Offermanse14b3422009-11-25 23:04:32 +0000279 // TODO lots of duplication in lookupService()
280 public ServiceReference lookupServiceReference() {
281 ServiceReference service = null;
282 if (m_isStarted) {
283 service = m_tracker.getServiceReference();
284 }
285 else {
286 ServiceReference[] refs = null;
287 ServiceReference ref = null;
288 if (m_trackedServiceName != null) {
289 if (m_trackedServiceFilter != null) {
290 try {
291 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
292 if (refs != null) {
293 Arrays.sort(refs, COMPARATOR);
294 ref = refs[0];
295 }
296 }
297 catch (InvalidSyntaxException e) {
298 throw new IllegalStateException("Invalid filter definition for dependency.");
299 }
300 }
301 else if (m_trackedServiceReference != null) {
302 ref = m_trackedServiceReference;
303 }
304 else {
305 ref = m_context.getServiceReference(m_trackedServiceName.getName());
306 }
307 if (ref != null) {
308 service = ref;
309 }
310 }
311 else {
312 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
313 }
314 }
315 return service;
316 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000317
318 private Object getNullObject() {
319 if (m_nullObject == null) {
320 Class trackedServiceName;
321 synchronized (this) {
322 trackedServiceName = m_trackedServiceName;
323 }
324 try {
325 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
326 }
327 catch (Exception e) {
328 m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
329 }
330 }
331 return m_nullObject;
332 }
333
334 private Object getDefaultImplementation() {
335 if (m_defaultImplementation != null) {
336 if (m_defaultImplementation instanceof Class) {
337 try {
338 m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
339 }
340 catch (Exception e) {
341 m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
342 }
343 }
344 else {
345 m_defaultImplementationInstance = m_defaultImplementation;
346 }
347 }
348 return m_defaultImplementationInstance;
349 }
350
351 public synchronized Class getInterface() {
352 return m_trackedServiceName;
353 }
354
Marcel Offermanse14b3422009-11-25 23:04:32 +0000355 public void start(DependencyService service) {
356 boolean needsStarting = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000357 synchronized (this) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000358 m_services.add(service);
359 if (!m_isStarted) {
360 if (m_trackedServiceName != null) {
361 if (m_trackedServiceFilter != null) {
362 try {
363 m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
364 }
365 catch (InvalidSyntaxException e) {
Marcel Offermansad760672010-03-03 15:30:01 +0000366 throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000367 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000368 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000369 else if (m_trackedServiceReference != null) {
370 m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000371 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000372 else {
373 m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
374 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000375 }
376 else {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000377 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000378 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000379 m_isStarted = true;
380 needsStarting = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000381 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000382 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000383 if (needsStarting) {
384 m_tracker.open();
385 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000386 }
387
Marcel Offermanse14b3422009-11-25 23:04:32 +0000388 public void stop(DependencyService service) {
389 boolean needsStopping = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000390 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000391 if (m_services.size() == 1 && m_services.contains(service)) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000392 m_isStarted = false;
393 needsStopping = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000394 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000395 m_services.remove(service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000396 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000397 if (needsStopping) {
398 m_tracker.close();
399 m_tracker = null;
400 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000401 }
402
403 public Object addingService(ServiceReference ref) {
404 Object service = m_context.getService(ref);
405 // first check to make sure the service is actually an instance of our service
406 if (!m_trackedServiceName.isInstance(service)) {
407 return null;
408 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000409 return service;
410 }
411
412 public void addedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000413 boolean makeAvailable = makeAvailable();
414
Marcel Offermans203bdad2009-12-04 09:23:04 +0000415 Object[] services;
416 synchronized (this) {
417 services = m_services.toArray();
418 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000419 for (int i = 0; i < services.length; i++) {
420 DependencyService ds = (DependencyService) services[i];
421 if (makeAvailable) {
Pierre De Rop1785c332010-06-11 06:03:53 +0000422 // The dependency callback will be defered until all required dependency are available.
423 ds.dependencyAvailable(this);
424 if (!isRequired()) {
425 // For optional dependency, we always invoke callback, because at this point, we know
426 // that the service has been started, and the service start method has been called.
427 // (See the ServiceImpl.bindService method, which will activate optional dependencies using
428 // startTrackingOptional() method).
Marcel Offermanse14b3422009-11-25 23:04:32 +0000429 invokeAdded(ds, ref, service);
430 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000431 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000432 else {
433 ds.dependencyChanged(this);
Pierre De Rop1785c332010-06-11 06:03:53 +0000434 // At this point, either the dependency is optional (meaning that the service has been started,
435 // because if not, then our dependency would not be active); or the dependency is required,
436 // meaning that either the service is not yet started, or already started.
437 // In all cases, we have to inject the required dependency.
Marcel Offermanse14b3422009-11-25 23:04:32 +0000438 invokeAdded(ds, ref, service);
439 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000440 }
441 }
442
Marcel Offermansa962bc92009-11-21 17:59:33 +0000443 public void modifiedService(ServiceReference ref, Object service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000444 Object[] services;
445 synchronized (this) {
446 services = m_services.toArray();
447 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000448 for (int i = 0; i < services.length; i++) {
449 DependencyService ds = (DependencyService) services[i];
450 ds.dependencyChanged(this);
451 if (ds.isRegistered()) {
452 invokeChanged(ds, ref, service);
453 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000454 }
455 }
456
Marcel Offermansa962bc92009-11-21 17:59:33 +0000457 public void removedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000458 boolean makeUnavailable = makeUnavailable();
459
Marcel Offermans203bdad2009-12-04 09:23:04 +0000460 Object[] services;
461 synchronized (this) {
462 services = m_services.toArray();
463 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000464 for (int i = 0; i < services.length; i++) {
465 DependencyService ds = (DependencyService) services[i];
466 if (makeUnavailable) {
467 ds.dependencyUnavailable(this);
468 if (!isRequired()) {
469 invokeRemoved(ds, ref, service);
470 }
471 }
472 else {
473 ds.dependencyChanged(this);
474 invokeRemoved(ds, ref, service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000475 }
476 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000477 // unget what we got in addingService (see ServiceTracker 701.4.1)
478 m_context.ungetService(ref);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000479
Marcel Offermansa962bc92009-11-21 17:59:33 +0000480 }
481
Marcel Offermansea89b862010-06-24 13:14:43 +0000482 public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
483 invoke(dependencyService, reference, service, m_callbackAdded);
484 }
485
486 public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
487 invoke(dependencyService, reference, service, m_callbackChanged);
488 }
489
Marcel Offermansb196d722009-11-26 17:12:12 +0000490 public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000491 invoke(dependencyService, reference, service, m_callbackRemoved);
492 }
493
494 public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
495 if (name != null) {
496 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
Marcel Offermansb196d722009-11-26 17:12:12 +0000497 new Class[][] {{ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}},
498 new Object[][] {{reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}}
499 );
Marcel Offermansa962bc92009-11-21 17:59:33 +0000500 }
501 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000502
Marcel Offermans74363c32009-11-23 19:56:08 +0000503 protected synchronized boolean makeAvailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000504 if (!isAvailable()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000505 m_isAvailable = true;
506 return true;
507 }
508 return false;
509 }
510
511 private synchronized boolean makeUnavailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000512 if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000513 m_isAvailable = false;
514 return true;
515 }
516 return false;
517 }
518
Marcel Offermanse14b3422009-11-25 23:04:32 +0000519 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
520 if (m_callbackInstance == null) {
521 return dependencyService.getCompositionInstances();
522 }
523 else {
524 return new Object[] { m_callbackInstance };
525 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000526 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000527
528 // ----- CREATION
529
530 /**
531 * Sets the name of the service that should be tracked.
532 *
533 * @param serviceName the name of the service
534 * @return this service dependency
535 */
536 public synchronized ServiceDependency setService(Class serviceName) {
537 ensureNotActive();
538 if (serviceName == null) {
539 throw new IllegalArgumentException("Service name cannot be null.");
540 }
541 m_trackedServiceName = serviceName;
542 m_trackedServiceReference = null;
543 m_trackedServiceFilter = null;
544 return this;
545 }
546
547 /**
548 * Sets the name of the service that should be tracked. You can either specify
549 * only the name, or the name and a filter. In the latter case, the filter is used
550 * to track the service and should only return services of the type that was specified
551 * in the name. To make sure of this, the filter is actually extended internally to
552 * filter on the correct name.
553 *
554 * @param serviceName the name of the service
555 * @param serviceFilter the filter condition
556 * @return this service dependency
557 */
558 public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
559 ensureNotActive();
560 if (serviceName == null) {
561 throw new IllegalArgumentException("Service name cannot be null.");
562 }
563 m_trackedServiceName = serviceName;
564 if (serviceFilter != null) {
565 m_trackedServiceFilterUnmodified = serviceFilter;
566 m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
567 }
568 else {
569 m_trackedServiceFilterUnmodified = null;
570 m_trackedServiceFilter = null;
571 }
572 m_trackedServiceReference = null;
573 return this;
574 }
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000575
576 public synchronized ServiceDependency setService(String serviceFilter) {
577 ensureNotActive();
578 if (serviceFilter == null) {
579 throw new IllegalArgumentException("Service filter cannot be null.");
580 }
581 m_trackedServiceName = Object.class;
582 if (serviceFilter != null) {
583 m_trackedServiceFilterUnmodified = serviceFilter;
584 m_trackedServiceFilter = serviceFilter;
585 }
586 m_trackedServiceReference = null;
587 return this;
588 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000589
590 /**
591 * Sets the name of the service that should be tracked. You can either specify
592 * only the name, or the name and a reference. In the latter case, the service reference
593 * is used to track the service and should only return services of the type that was
594 * specified in the name.
595 *
596 * @param serviceName the name of the service
597 * @param serviceReference the service reference to track
598 * @return this service dependency
599 */
600 public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
601 ensureNotActive();
602 if (serviceName == null) {
603 throw new IllegalArgumentException("Service name cannot be null.");
604 }
605 m_trackedServiceName = serviceName;
606 m_trackedServiceReference = serviceReference;
607 m_trackedServiceFilterUnmodified = null;
608 m_trackedServiceFilter = null;
609 return this;
610 }
611
612 /**
613 * Sets the default implementation for this service dependency. You can use this to supply
614 * your own implementation that will be used instead of a Null Object when the dependency is
615 * not available. This is also convenient if the service dependency is not an interface
616 * (which would cause the Null Object creation to fail) but a class.
617 *
618 * @param implementation the instance to use or the class to instantiate if you want to lazily
619 * instantiate this implementation
620 * @return this service dependency
621 */
622 public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
623 ensureNotActive();
624 m_defaultImplementation = implementation;
625 return this;
626 }
627
628 /**
629 * Sets the required flag which determines if this service is required or not.
630 *
631 * @param required the required flag
632 * @return this service dependency
633 */
634 public synchronized ServiceDependency setRequired(boolean required) {
635 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000636 setIsRequired(required);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000637 return this;
638 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000639
640 public ServiceDependency setInstanceBound(boolean isInstanceBound) {
Marcel Offermans61a81142010-04-02 15:16:50 +0000641 setIsInstanceBound(isInstanceBound);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000642 return this;
643 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000644
645 /**
646 * Sets auto configuration for this service. Auto configuration allows the
647 * dependency to fill in any attributes in the service implementation that
648 * are of the same type as this dependency. Default is on.
649 *
650 * @param autoConfig the value of auto config
651 * @return this service dependency
652 */
653 public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
654 ensureNotActive();
655 m_autoConfig = autoConfig;
656 m_autoConfigInvoked = true;
657 return this;
658 }
659
660 /**
661 * Sets auto configuration for this service. Auto configuration allows the
662 * dependency to fill in the attribute in the service implementation that
663 * has the same type and instance name.
664 *
665 * @param instanceName the name of attribute to auto config
666 * @return this service dependency
667 */
668 public synchronized ServiceDependency setAutoConfig(String instanceName) {
669 ensureNotActive();
670 m_autoConfig = (instanceName != null);
671 m_autoConfigInstance = instanceName;
672 m_autoConfigInvoked = true;
673 return this;
674 }
675
676 /**
677 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
678 * dependency is added or removed. When you specify callbacks, the auto configuration
679 * feature is automatically turned off, because we're assuming you don't need it in this
680 * case.
681 *
682 * @param added the method to call when a service was added
683 * @param removed the method to call when a service was removed
684 * @return this service dependency
685 */
686 public synchronized ServiceDependency setCallbacks(String added, String removed) {
687 return setCallbacks(null, added, null, removed);
688 }
689
690 /**
691 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
692 * dependency is added, changed or removed. When you specify callbacks, the auto
693 * configuration feature is automatically turned off, because we're assuming you don't
694 * need it in this case.
695 *
696 * @param added the method to call when a service was added
697 * @param changed the method to call when a service was changed
698 * @param removed the method to call when a service was removed
699 * @return this service dependency
700 */
701 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
702 return setCallbacks(null, added, changed, removed);
703 }
704
705 /**
706 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
707 * dependency is added or removed. They are called on the instance you provide. When you
708 * specify callbacks, the auto configuration feature is automatically turned off, because
709 * we're assuming you don't need it in this case.
710 *
711 * @param instance the instance to call the callbacks on
712 * @param added the method to call when a service was added
713 * @param removed the method to call when a service was removed
714 * @return this service dependency
715 */
716 public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
717 return setCallbacks(instance, added, null, removed);
718 }
719
720 /**
721 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
722 * dependency is added, changed or removed. They are called on the instance you provide. When you
723 * specify callbacks, the auto configuration feature is automatically turned off, because
724 * we're assuming you don't need it in this case.
725 *
726 * @param instance the instance to call the callbacks on
727 * @param added the method to call when a service was added
728 * @param changed the method to call when a service was changed
729 * @param removed the method to call when a service was removed
730 * @return this service dependency
731 */
732 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
733 ensureNotActive();
734 // if at least one valid callback is specified, we turn off auto configuration, unless
735 // someone already explicitly invoked autoConfig
736 if ((added != null || removed != null || changed != null) && ! m_autoConfigInvoked) {
737 setAutoConfig(false);
738 }
739 m_callbackInstance = instance;
740 m_callbackAdded = added;
741 m_callbackChanged = changed;
742 m_callbackRemoved = removed;
743 return this;
744 }
745
746 private void ensureNotActive() {
747 if (m_tracker != null) {
748 throw new IllegalStateException("Cannot modify state while active.");
749 }
750 }
751
752 public synchronized String toString() {
753 return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
754 }
755
756 public String getAutoConfigName() {
757 return m_autoConfigInstance;
758 }
Marcel Offermans001db052009-12-08 08:58:40 +0000759
760 public Object getAutoConfigInstance() {
761 return lookupService();
762 }
763
764 public Class getAutoConfigType() {
765 return getInterface();
766 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000767
768 public String getName() {
769 StringBuilder sb = new StringBuilder();
Marcel Offermansb1959f42010-07-01 12:23:51 +0000770 if (m_trackedServiceName != null) {
771 sb.append(m_trackedServiceName.getName());
772 if (m_trackedServiceFilterUnmodified != null) {
773 sb.append(' ');
774 sb.append(m_trackedServiceFilterUnmodified);
775 }
776 }
777 if (m_trackedServiceReference != null) {
Marcel Offermans9e50ef32010-10-07 11:37:00 +0000778 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000779 }
780 return sb.toString();
781 }
782
783 public int getState() {
784 return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
785 }
786
787 public String getType() {
788 return "service";
789 }
Marcel Offermans001db052009-12-08 08:58:40 +0000790
791 public void invokeAdded(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +0000792 ServiceReference[] refs = m_tracker.getServiceReferences();
793 if (refs != null) {
794 for (int i = 0; i < refs.length; i++) {
795 ServiceReference sr = refs[i];
796 Object svc = m_context.getService(sr);
797 invokeAdded(service, sr, svc);
798 }
799 }
800 m_references = refs;
Marcel Offermans001db052009-12-08 08:58:40 +0000801 }
Marcel Offermansbde9a432010-05-11 08:01:28 +0000802
Marcel Offermans001db052009-12-08 08:58:40 +0000803 public void invokeRemoved(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +0000804 ServiceReference[] refs = m_references;
805 if (refs != null) {
806 for (int i = 0; i < refs.length; i++) {
807 ServiceReference sr = refs[i];
808 Object svc = m_context.getService(sr);
809 invokeRemoved(service, sr, svc);
810 }
811 }
812 m_references = null;
Marcel Offermans001db052009-12-08 08:58:40 +0000813 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000814
815 public Dictionary getProperties() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000816 ServiceReference reference = lookupServiceReference();
817 Object service = lookupService();
818 if (reference != null) {
819 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
820 try {
821 return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
822 }
823 catch (InvocationTargetException e) {
824 m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
825 }
826 catch (Exception e) {
827 m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
828 }
829 throw new IllegalStateException("Could not invoke callback");
830 }
831 else {
832 Properties props = new Properties();
833 String[] keys = reference.getPropertyKeys();
834 for (int i = 0; i < keys.length; i++) {
835 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
836 props.put(keys[i], reference.getProperty(keys[i]));
837 }
838 }
839 return props;
840 }
841 }
842 else {
843 throw new IllegalStateException("cannot find service reference");
844 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000845 }
846
847 public boolean isPropagated() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000848 return m_propagate;
849 }
850
851 public ServiceDependency setPropagate(boolean propagate) {
852 ensureNotActive();
853 m_propagate = propagate;
854 return this;
855 }
856
857 public ServiceDependency setPropagate(Object instance, String method) {
858 setPropagate(instance != null && method != null);
859 m_propagateCallbackInstance = instance;
860 m_propagateCallbackMethod = method;
861 return this;
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000862 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000863}