blob: e89dab071801b1f2946c40d4a65f0a557fff31dc [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 Offermanse14b3422009-11-25 23:04:32 +000030import java.util.List;
Marcel Offermansa962bc92009-11-21 17:59:33 +000031import java.util.Map;
Marcel Offermans26081d32010-07-12 12:43:42 +000032import java.util.Properties;
Marcel Offermansa962bc92009-11-21 17:59:33 +000033import java.util.Set;
34
Marcel Offermans706fb272010-11-15 12:52:58 +000035import org.apache.felix.dm.Component;
Marcel Offermans9e50ef32010-10-07 11:37:00 +000036import org.apache.felix.dm.ComponentDependencyDeclaration;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000037import org.apache.felix.dm.Dependency;
Marcel Offermans3d921212010-08-09 13:37:02 +000038import org.apache.felix.dm.DependencyService;
Marcel Offermans706fb272010-11-15 12:52:58 +000039import org.apache.felix.dm.InvocationUtil;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000040import org.apache.felix.dm.ServiceDependency;
Pierre De Ropaacb3aa2009-12-04 22:53:23 +000041import org.apache.felix.dm.impl.DefaultNullObject;
42import org.apache.felix.dm.impl.Logger;
Marcel Offermansfaaed472010-09-08 10:07:32 +000043import org.apache.felix.dm.tracker.ServiceTracker;
44import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
Marcel Offermansa962bc92009-11-21 17:59:33 +000045import org.osgi.framework.BundleContext;
46import org.osgi.framework.Constants;
47import org.osgi.framework.InvalidSyntaxException;
48import org.osgi.framework.ServiceReference;
Marcel Offermans26081d32010-07-12 12:43:42 +000049import org.osgi.service.log.LogService;
Marcel Offermansa962bc92009-11-21 17:59:33 +000050
51/**
52 * Service dependency that can track an OSGi service.
53 *
54 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
55 */
Marcel Offermansfaaed472010-09-08 10:07:32 +000056public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
Marcel Offermanse14b3422009-11-25 23:04:32 +000057 protected List m_services = new ArrayList();
Marcel Offermans74363c32009-11-23 19:56:08 +000058 protected volatile ServiceTracker m_tracker;
59 protected BundleContext m_context;
Marcel Offermans74363c32009-11-23 19:56:08 +000060 protected volatile Class m_trackedServiceName;
Marcel Offermansa962bc92009-11-21 17:59:33 +000061 private Object m_nullObject;
62 private volatile String m_trackedServiceFilter;
63 private volatile String m_trackedServiceFilterUnmodified;
64 private volatile ServiceReference m_trackedServiceReference;
65 private volatile boolean m_isStarted;
66 private Object m_callbackInstance;
67 private String m_callbackAdded;
68 private String m_callbackChanged;
69 private String m_callbackRemoved;
70 private boolean m_autoConfig;
Marcel Offermans74363c32009-11-23 19:56:08 +000071 protected ServiceReference m_reference;
72 protected Object m_serviceInstance;
Marcel Offermansa962bc92009-11-21 17:59:33 +000073 private String m_autoConfigInstance;
74 private boolean m_autoConfigInvoked;
75 private Object m_defaultImplementation;
76 private Object m_defaultImplementationInstance;
Marcel Offermansb196d722009-11-26 17:12:12 +000077 private boolean m_isAvailable;
Marcel Offermans26081d32010-07-12 12:43:42 +000078 private boolean m_propagate;
79 private Object m_propagateCallbackInstance;
80 private String m_propagateCallbackMethod;
Marcel Offermans7784e402011-01-21 20:41:50 +000081 private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
Marcel Offermansa962bc92009-11-21 17:59:33 +000082
83 private static final Comparator COMPARATOR = new Comparator() {
84 public int getRank(ServiceReference ref) {
85 Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
86 if (ranking != null && (ranking instanceof Integer)) {
87 return ((Integer) ranking).intValue();
88 }
89 return 0;
90 }
91
92 public int compare(Object a, Object b) {
93 ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
94 int ranka = getRank(ra);
95 int rankb = getRank(rb);
96 if (ranka < rankb) {
97 return -1;
98 }
99 else if (ranka > rankb) {
100 return 1;
101 }
102 return 0;
103 }
104 };
105
Marcel Offermans7784e402011-01-21 20:41:50 +0000106 private static final class Tuple /* <ServiceReference, Object> */ {
107 private final ServiceReference m_serviceReference;
108 private final Object m_service;
109
110 public Tuple(ServiceReference first, Object last) {
111 m_serviceReference = first;
112 m_service = last;
113 }
114
115 public ServiceReference getServiceReference() {
116 return m_serviceReference;
117 }
118
119 public Object getService() {
120 return m_service;
121 }
122
123 public boolean equals(Object obj) {
124 return ((Tuple) obj).getServiceReference().equals(getServiceReference());
125 }
126
127 public int hashCode() {
128 return m_serviceReference.hashCode();
129 }
130 }
131
Marcel Offermansa962bc92009-11-21 17:59:33 +0000132 /**
133 * Entry to wrap service properties behind a Map.
134 */
Marcel Offermans7784e402011-01-21 20:41:50 +0000135 private static final class ServicePropertiesMapEntry implements Map.Entry {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000136 private final String m_key;
137 private Object m_value;
138
139 public ServicePropertiesMapEntry(String key, Object value) {
140 m_key = key;
141 m_value = value;
142 }
143
144 public Object getKey() {
145 return m_key;
146 }
147
148 public Object getValue() {
149 return m_value;
150 }
151
152 public String toString() {
153 return m_key + "=" + m_value;
154 }
155
156 public Object setValue(Object value) {
157 Object oldValue = m_value;
158 m_value = value;
159 return oldValue;
160 }
161
162 public boolean equals(Object o) {
163 if (!(o instanceof Map.Entry)) {
164 return false;
165 }
166 Map.Entry e = (Map.Entry) o;
167 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
168 }
169
170 public int hashCode() {
171 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
172 }
173
174 private static final boolean eq(Object o1, Object o2) {
175 return (o1 == null ? o2 == null : o1.equals(o2));
176 }
177 }
178
179 /**
180 * Wraps service properties behind a Map.
181 */
182 private final static class ServicePropertiesMap extends AbstractMap {
183 private final ServiceReference m_ref;
184
185 public ServicePropertiesMap(ServiceReference ref) {
186 m_ref = ref;
187 }
188
189 public Object get(Object key) {
190 return m_ref.getProperty(key.toString());
191 }
192
193 public int size() {
194 return m_ref.getPropertyKeys().length;
195 }
196
197 public Set entrySet() {
198 Set set = new HashSet();
199 String[] keys = m_ref.getPropertyKeys();
200 for (int i = 0; i < keys.length; i++) {
201 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
202 }
203 return set;
204 }
205 }
206
207 /**
208 * Creates a new service dependency.
209 *
210 * @param context the bundle context
211 * @param logger the logger
212 */
Pierre De Ropaacb3aa2009-12-04 22:53:23 +0000213 public ServiceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +0000214 super(logger);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000215 m_context = context;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000216 m_autoConfig = true;
217 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000218
Marcel Offermansb1959f42010-07-01 12:23:51 +0000219 /** Copying constructor that clones an existing instance. */
220 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
221 super(prototype);
Marcel Offermanse7baff52010-12-17 08:51:52 +0000222 synchronized (prototype) {
223 m_context = prototype.m_context;
224 m_autoConfig = prototype.m_autoConfig;
225 m_trackedServiceName = prototype.m_trackedServiceName;
226 m_nullObject = prototype.m_nullObject;
227 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
228 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
229 m_trackedServiceReference = prototype.m_trackedServiceReference;
230 m_callbackInstance = prototype.m_callbackInstance;
231 m_callbackAdded = prototype.m_callbackAdded;
232 m_callbackChanged = prototype.m_callbackChanged;
233 m_callbackRemoved = prototype.m_callbackRemoved;
234 m_autoConfigInstance = prototype.m_autoConfigInstance;
235 m_defaultImplementation = prototype.m_defaultImplementation;
236 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000237 }
238
239 public Dependency createCopy() {
240 return new ServiceDependencyImpl(this);
241 }
242
Marcel Offermansa962bc92009-11-21 17:59:33 +0000243 public synchronized boolean isAutoConfig() {
244 return m_autoConfig;
245 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000246
Marcel Offermansb196d722009-11-26 17:12:12 +0000247 public synchronized boolean isAvailable() {
248 return m_isAvailable;
249 }
250
Marcel Offermansa962bc92009-11-21 17:59:33 +0000251 public synchronized Object getService() {
252 Object service = null;
253 if (m_isStarted) {
254 service = m_tracker.getService();
255 }
Marcel Offermans001db052009-12-08 08:58:40 +0000256 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000257 service = getDefaultImplementation();
258 if (service == null) {
259 service = getNullObject();
260 }
261 }
262 return service;
263 }
264
265 public Object lookupService() {
266 Object service = null;
267 if (m_isStarted) {
Marcel Offermans001db052009-12-08 08:58:40 +0000268 service = getService();
Marcel Offermansa962bc92009-11-21 17:59:33 +0000269 }
270 else {
271 ServiceReference[] refs = null;
272 ServiceReference ref = null;
273 if (m_trackedServiceName != null) {
274 if (m_trackedServiceFilter != null) {
275 try {
276 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
277 if (refs != null) {
278 Arrays.sort(refs, COMPARATOR);
279 ref = refs[0];
280 }
281 }
282 catch (InvalidSyntaxException e) {
283 throw new IllegalStateException("Invalid filter definition for dependency.");
284 }
285 }
286 else if (m_trackedServiceReference != null) {
287 ref = m_trackedServiceReference;
288 }
289 else {
290 ref = m_context.getServiceReference(m_trackedServiceName.getName());
291 }
292 if (ref != null) {
293 service = m_context.getService(ref);
294 }
295 }
296 else {
297 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
298 }
299 }
Marcel Offermans001db052009-12-08 08:58:40 +0000300 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000301 service = getDefaultImplementation();
302 if (service == null) {
303 service = getNullObject();
304 }
305 }
306 return service;
307 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000308
Marcel Offermanse14b3422009-11-25 23:04:32 +0000309 // TODO lots of duplication in lookupService()
310 public ServiceReference lookupServiceReference() {
311 ServiceReference service = null;
312 if (m_isStarted) {
313 service = m_tracker.getServiceReference();
314 }
315 else {
316 ServiceReference[] refs = null;
317 ServiceReference ref = null;
318 if (m_trackedServiceName != null) {
319 if (m_trackedServiceFilter != null) {
320 try {
321 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
322 if (refs != null) {
323 Arrays.sort(refs, COMPARATOR);
324 ref = refs[0];
325 }
326 }
327 catch (InvalidSyntaxException e) {
328 throw new IllegalStateException("Invalid filter definition for dependency.");
329 }
330 }
331 else if (m_trackedServiceReference != null) {
332 ref = m_trackedServiceReference;
333 }
334 else {
335 ref = m_context.getServiceReference(m_trackedServiceName.getName());
336 }
337 if (ref != null) {
338 service = ref;
339 }
340 }
341 else {
342 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
343 }
344 }
345 return service;
346 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000347
348 private Object getNullObject() {
349 if (m_nullObject == null) {
350 Class trackedServiceName;
351 synchronized (this) {
352 trackedServiceName = m_trackedServiceName;
353 }
354 try {
355 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
356 }
357 catch (Exception e) {
358 m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
359 }
360 }
361 return m_nullObject;
362 }
363
364 private Object getDefaultImplementation() {
365 if (m_defaultImplementation != null) {
366 if (m_defaultImplementation instanceof Class) {
367 try {
368 m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
369 }
370 catch (Exception e) {
371 m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
372 }
373 }
374 else {
375 m_defaultImplementationInstance = m_defaultImplementation;
376 }
377 }
378 return m_defaultImplementationInstance;
379 }
380
381 public synchronized Class getInterface() {
382 return m_trackedServiceName;
383 }
384
Marcel Offermanse14b3422009-11-25 23:04:32 +0000385 public void start(DependencyService service) {
386 boolean needsStarting = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000387 synchronized (this) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000388 m_services.add(service);
389 if (!m_isStarted) {
390 if (m_trackedServiceName != null) {
391 if (m_trackedServiceFilter != null) {
392 try {
393 m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
394 }
395 catch (InvalidSyntaxException e) {
Marcel Offermansad760672010-03-03 15:30:01 +0000396 throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000397 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000398 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000399 else if (m_trackedServiceReference != null) {
400 m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000401 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000402 else {
403 m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
404 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000405 }
406 else {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000407 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000408 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000409 m_isStarted = true;
410 needsStarting = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000411 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000412 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000413 if (needsStarting) {
414 m_tracker.open();
415 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000416 }
417
Marcel Offermanse14b3422009-11-25 23:04:32 +0000418 public void stop(DependencyService service) {
419 boolean needsStopping = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000420 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000421 if (m_services.size() == 1 && m_services.contains(service)) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000422 m_isStarted = false;
423 needsStopping = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000424 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000425 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000426 if (needsStopping) {
427 m_tracker.close();
428 m_tracker = null;
429 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000430 //moved this down
431 synchronized (this) {
432 m_services.remove(service);
433 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000434 }
435
436 public Object addingService(ServiceReference ref) {
437 Object service = m_context.getService(ref);
438 // first check to make sure the service is actually an instance of our service
439 if (!m_trackedServiceName.isInstance(service)) {
440 return null;
441 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000442 return service;
443 }
444
445 public void addedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000446 boolean makeAvailable = makeAvailable();
447
Marcel Offermans203bdad2009-12-04 09:23:04 +0000448 Object[] services;
449 synchronized (this) {
450 services = m_services.toArray();
451 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000452 for (int i = 0; i < services.length; i++) {
453 DependencyService ds = (DependencyService) services[i];
454 if (makeAvailable) {
Marcel Offermansfea14842010-10-14 14:45:36 +0000455 if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
456 invokeAdded(ds, ref, service);
457 }
Pierre De Rop1785c332010-06-11 06:03:53 +0000458 // The dependency callback will be defered until all required dependency are available.
459 ds.dependencyAvailable(this);
460 if (!isRequired()) {
461 // For optional dependency, we always invoke callback, because at this point, we know
462 // that the service has been started, and the service start method has been called.
463 // (See the ServiceImpl.bindService method, which will activate optional dependencies using
464 // startTrackingOptional() method).
Marcel Offermanse14b3422009-11-25 23:04:32 +0000465 invokeAdded(ds, ref, service);
466 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000467 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000468 else {
469 ds.dependencyChanged(this);
Pierre De Rop1785c332010-06-11 06:03:53 +0000470 // At this point, either the dependency is optional (meaning that the service has been started,
471 // because if not, then our dependency would not be active); or the dependency is required,
472 // meaning that either the service is not yet started, or already started.
473 // In all cases, we have to inject the required dependency.
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000474
475 // we only try to invoke the method here if we are really already instantiated
476 if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
477 invokeAdded(ds, ref, service);
478 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000479 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000480 }
481 }
482
Marcel Offermansa962bc92009-11-21 17:59:33 +0000483 public void modifiedService(ServiceReference ref, Object service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000484 Object[] services;
485 synchronized (this) {
486 services = m_services.toArray();
487 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000488 for (int i = 0; i < services.length; i++) {
489 DependencyService ds = (DependencyService) services[i];
490 ds.dependencyChanged(this);
491 if (ds.isRegistered()) {
492 invokeChanged(ds, ref, service);
493 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000494 }
495 }
496
Marcel Offermansa962bc92009-11-21 17:59:33 +0000497 public void removedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000498 boolean makeUnavailable = makeUnavailable();
499
Marcel Offermans203bdad2009-12-04 09:23:04 +0000500 Object[] services;
501 synchronized (this) {
502 services = m_services.toArray();
503 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000504
Marcel Offermanse14b3422009-11-25 23:04:32 +0000505 for (int i = 0; i < services.length; i++) {
506 DependencyService ds = (DependencyService) services[i];
507 if (makeUnavailable) {
508 ds.dependencyUnavailable(this);
Marcel Offermansfea14842010-10-14 14:45:36 +0000509 if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000510 invokeRemoved(ds, ref, service);
511 }
512 }
513 else {
514 ds.dependencyChanged(this);
515 invokeRemoved(ds, ref, service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000516 }
517 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000518 // unget what we got in addingService (see ServiceTracker 701.4.1)
519 m_context.ungetService(ref);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000520
Marcel Offermansa962bc92009-11-21 17:59:33 +0000521 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000522
Marcel Offermansea89b862010-06-24 13:14:43 +0000523 public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +0000524 boolean added = false;
525 synchronized (m_sr) {
526 Set set = (Set) m_sr.get(dependencyService);
527 if (set == null) {
528 set = new HashSet();
529 m_sr.put(dependencyService, set);
530 }
Marcel Offermans7784e402011-01-21 20:41:50 +0000531 added = set.add(new Tuple(reference, service));
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000532 }
Marcel Offermanse7baff52010-12-17 08:51:52 +0000533 if (added) {
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000534 invoke(dependencyService, reference, service, m_callbackAdded);
535 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000536 }
537
538 public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
539 invoke(dependencyService, reference, service, m_callbackChanged);
540 }
541
Marcel Offermansb196d722009-11-26 17:12:12 +0000542 public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +0000543 boolean removed = false;
544 synchronized (m_sr) {
545 Set set = (Set) m_sr.get(dependencyService);
Marcel Offermans7784e402011-01-21 20:41:50 +0000546 removed = (set != null && set.remove(new Tuple(reference, service)));
Marcel Offermanse7baff52010-12-17 08:51:52 +0000547 }
548 if (removed) {
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000549 invoke(dependencyService, reference, service, m_callbackRemoved);
550 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000551 }
552
553 public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
554 if (name != null) {
555 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
Marcel Offermans706fb272010-11-15 12:52:58 +0000556 new Class[][] {
557 {Component.class, ServiceReference.class, m_trackedServiceName}, {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class}, {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class}, {Component.class, Map.class, m_trackedServiceName},
558 {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
559 },
560 new Object[][] {
561 {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
562 {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
563 }
Marcel Offermansb196d722009-11-26 17:12:12 +0000564 );
Marcel Offermansa962bc92009-11-21 17:59:33 +0000565 }
566 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000567
Marcel Offermans74363c32009-11-23 19:56:08 +0000568 protected synchronized boolean makeAvailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000569 if (!isAvailable()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000570 m_isAvailable = true;
571 return true;
572 }
573 return false;
574 }
575
576 private synchronized boolean makeUnavailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000577 if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000578 m_isAvailable = false;
579 return true;
580 }
581 return false;
582 }
583
Marcel Offermanse14b3422009-11-25 23:04:32 +0000584 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
585 if (m_callbackInstance == null) {
586 return dependencyService.getCompositionInstances();
587 }
588 else {
589 return new Object[] { m_callbackInstance };
590 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000591 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000592
593 // ----- CREATION
594
595 /**
596 * Sets the name of the service that should be tracked.
597 *
598 * @param serviceName the name of the service
599 * @return this service dependency
600 */
601 public synchronized ServiceDependency setService(Class serviceName) {
602 ensureNotActive();
603 if (serviceName == null) {
604 throw new IllegalArgumentException("Service name cannot be null.");
605 }
606 m_trackedServiceName = serviceName;
607 m_trackedServiceReference = null;
608 m_trackedServiceFilter = null;
609 return this;
610 }
611
612 /**
613 * Sets the name of the service that should be tracked. You can either specify
614 * only the name, or the name and a filter. In the latter case, the filter is used
615 * to track the service and should only return services of the type that was specified
616 * in the name. To make sure of this, the filter is actually extended internally to
617 * filter on the correct name.
618 *
619 * @param serviceName the name of the service
620 * @param serviceFilter the filter condition
621 * @return this service dependency
622 */
623 public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
624 ensureNotActive();
625 if (serviceName == null) {
626 throw new IllegalArgumentException("Service name cannot be null.");
627 }
628 m_trackedServiceName = serviceName;
629 if (serviceFilter != null) {
630 m_trackedServiceFilterUnmodified = serviceFilter;
631 m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
632 }
633 else {
634 m_trackedServiceFilterUnmodified = null;
635 m_trackedServiceFilter = null;
636 }
637 m_trackedServiceReference = null;
638 return this;
639 }
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000640
641 public synchronized ServiceDependency setService(String serviceFilter) {
642 ensureNotActive();
643 if (serviceFilter == null) {
644 throw new IllegalArgumentException("Service filter cannot be null.");
645 }
646 m_trackedServiceName = Object.class;
647 if (serviceFilter != null) {
648 m_trackedServiceFilterUnmodified = serviceFilter;
649 m_trackedServiceFilter = serviceFilter;
650 }
651 m_trackedServiceReference = null;
652 return this;
653 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000654
655 /**
656 * Sets the name of the service that should be tracked. You can either specify
657 * only the name, or the name and a reference. In the latter case, the service reference
658 * is used to track the service and should only return services of the type that was
659 * specified in the name.
660 *
661 * @param serviceName the name of the service
662 * @param serviceReference the service reference to track
663 * @return this service dependency
664 */
665 public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
666 ensureNotActive();
667 if (serviceName == null) {
668 throw new IllegalArgumentException("Service name cannot be null.");
669 }
670 m_trackedServiceName = serviceName;
671 m_trackedServiceReference = serviceReference;
672 m_trackedServiceFilterUnmodified = null;
673 m_trackedServiceFilter = null;
674 return this;
675 }
676
677 /**
678 * Sets the default implementation for this service dependency. You can use this to supply
679 * your own implementation that will be used instead of a Null Object when the dependency is
680 * not available. This is also convenient if the service dependency is not an interface
681 * (which would cause the Null Object creation to fail) but a class.
682 *
683 * @param implementation the instance to use or the class to instantiate if you want to lazily
684 * instantiate this implementation
685 * @return this service dependency
686 */
687 public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
688 ensureNotActive();
689 m_defaultImplementation = implementation;
690 return this;
691 }
692
693 /**
694 * Sets the required flag which determines if this service is required or not.
695 *
696 * @param required the required flag
697 * @return this service dependency
698 */
699 public synchronized ServiceDependency setRequired(boolean required) {
700 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000701 setIsRequired(required);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000702 return this;
703 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000704
705 public ServiceDependency setInstanceBound(boolean isInstanceBound) {
Marcel Offermans61a81142010-04-02 15:16:50 +0000706 setIsInstanceBound(isInstanceBound);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000707 return this;
708 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000709
710 /**
711 * Sets auto configuration for this service. Auto configuration allows the
712 * dependency to fill in any attributes in the service implementation that
713 * are of the same type as this dependency. Default is on.
714 *
715 * @param autoConfig the value of auto config
716 * @return this service dependency
717 */
718 public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
719 ensureNotActive();
720 m_autoConfig = autoConfig;
721 m_autoConfigInvoked = true;
722 return this;
723 }
724
725 /**
726 * Sets auto configuration for this service. Auto configuration allows the
727 * dependency to fill in the attribute in the service implementation that
728 * has the same type and instance name.
729 *
730 * @param instanceName the name of attribute to auto config
731 * @return this service dependency
732 */
733 public synchronized ServiceDependency setAutoConfig(String instanceName) {
734 ensureNotActive();
735 m_autoConfig = (instanceName != null);
736 m_autoConfigInstance = instanceName;
737 m_autoConfigInvoked = true;
738 return this;
739 }
740
741 /**
742 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
743 * dependency is added or removed. When you specify callbacks, the auto configuration
744 * feature is automatically turned off, because we're assuming you don't need it in this
745 * case.
746 *
747 * @param added the method to call when a service was added
748 * @param removed the method to call when a service was removed
749 * @return this service dependency
750 */
751 public synchronized ServiceDependency setCallbacks(String added, String removed) {
752 return setCallbacks(null, added, null, removed);
753 }
754
755 /**
756 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
757 * dependency is added, changed or removed. When you specify callbacks, the auto
758 * configuration feature is automatically turned off, because we're assuming you don't
759 * need it in this case.
760 *
761 * @param added the method to call when a service was added
762 * @param changed the method to call when a service was changed
763 * @param removed the method to call when a service was removed
764 * @return this service dependency
765 */
766 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
767 return setCallbacks(null, added, changed, removed);
768 }
769
770 /**
771 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
772 * dependency is added or removed. They are called on the instance you provide. When you
773 * specify callbacks, the auto configuration feature is automatically turned off, because
774 * we're assuming you don't need it in this case.
775 *
776 * @param instance the instance to call the callbacks on
777 * @param added the method to call when a service was added
778 * @param removed the method to call when a service was removed
779 * @return this service dependency
780 */
781 public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
782 return setCallbacks(instance, added, null, removed);
783 }
784
785 /**
786 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
787 * dependency is added, changed or removed. They are called on the instance you provide. When you
788 * specify callbacks, the auto configuration feature is automatically turned off, because
789 * we're assuming you don't need it in this case.
790 *
791 * @param instance the instance to call the callbacks on
792 * @param added the method to call when a service was added
793 * @param changed the method to call when a service was changed
794 * @param removed the method to call when a service was removed
795 * @return this service dependency
796 */
797 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
798 ensureNotActive();
799 // if at least one valid callback is specified, we turn off auto configuration, unless
800 // someone already explicitly invoked autoConfig
801 if ((added != null || removed != null || changed != null) && ! m_autoConfigInvoked) {
802 setAutoConfig(false);
803 }
804 m_callbackInstance = instance;
805 m_callbackAdded = added;
806 m_callbackChanged = changed;
807 m_callbackRemoved = removed;
808 return this;
809 }
810
811 private void ensureNotActive() {
812 if (m_tracker != null) {
813 throw new IllegalStateException("Cannot modify state while active.");
814 }
815 }
816
817 public synchronized String toString() {
818 return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
819 }
820
821 public String getAutoConfigName() {
822 return m_autoConfigInstance;
823 }
Marcel Offermans001db052009-12-08 08:58:40 +0000824
825 public Object getAutoConfigInstance() {
826 return lookupService();
827 }
828
829 public Class getAutoConfigType() {
830 return getInterface();
831 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000832
833 public String getName() {
834 StringBuilder sb = new StringBuilder();
Marcel Offermansb1959f42010-07-01 12:23:51 +0000835 if (m_trackedServiceName != null) {
836 sb.append(m_trackedServiceName.getName());
837 if (m_trackedServiceFilterUnmodified != null) {
838 sb.append(' ');
839 sb.append(m_trackedServiceFilterUnmodified);
840 }
841 }
842 if (m_trackedServiceReference != null) {
Marcel Offermans9e50ef32010-10-07 11:37:00 +0000843 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000844 }
845 return sb.toString();
846 }
847
848 public int getState() {
849 return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
850 }
851
852 public String getType() {
853 return "service";
854 }
Marcel Offermans001db052009-12-08 08:58:40 +0000855
856 public void invokeAdded(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +0000857 ServiceReference[] refs = m_tracker.getServiceReferences();
858 if (refs != null) {
859 for (int i = 0; i < refs.length; i++) {
860 ServiceReference sr = refs[i];
861 Object svc = m_context.getService(sr);
862 invokeAdded(service, sr, svc);
863 }
864 }
Marcel Offermans001db052009-12-08 08:58:40 +0000865 }
Marcel Offermansbde9a432010-05-11 08:01:28 +0000866
Marcel Offermans001db052009-12-08 08:58:40 +0000867 public void invokeRemoved(DependencyService service) {
Marcel Offermanse7baff52010-12-17 08:51:52 +0000868 Set references = null;
869 synchronized (m_sr) {
870 references = (Set) m_sr.get(service);
871 }
Marcel Offermans7784e402011-01-21 20:41:50 +0000872 Tuple[] refs = (Tuple[]) (references != null ? references.toArray(new Tuple[references.size()]) : new Tuple[0]);
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000873
874 for (int i = 0; i < refs.length; i++) {
Marcel Offermans7784e402011-01-21 20:41:50 +0000875 ServiceReference sr = refs[i].getServiceReference();
876 Object svc = refs[i].getService();
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000877 invokeRemoved(service, sr, svc);
Marcel Offermansbde9a432010-05-11 08:01:28 +0000878 }
Marcel Offermans09e14aa2010-11-25 13:36:23 +0000879 if (references != null) {
880 references.clear();
881 }
Marcel Offermans001db052009-12-08 08:58:40 +0000882 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000883
884 public Dictionary getProperties() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000885 ServiceReference reference = lookupServiceReference();
886 Object service = lookupService();
887 if (reference != null) {
888 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
889 try {
890 return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
891 }
892 catch (InvocationTargetException e) {
893 m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
894 }
895 catch (Exception e) {
896 m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
897 }
898 throw new IllegalStateException("Could not invoke callback");
899 }
900 else {
901 Properties props = new Properties();
902 String[] keys = reference.getPropertyKeys();
903 for (int i = 0; i < keys.length; i++) {
904 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
905 props.put(keys[i], reference.getProperty(keys[i]));
906 }
907 }
908 return props;
909 }
910 }
911 else {
912 throw new IllegalStateException("cannot find service reference");
913 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000914 }
915
916 public boolean isPropagated() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000917 return m_propagate;
918 }
919
920 public ServiceDependency setPropagate(boolean propagate) {
921 ensureNotActive();
922 m_propagate = propagate;
923 return this;
924 }
925
926 public ServiceDependency setPropagate(Object instance, String method) {
927 setPropagate(instance != null && method != null);
928 m_propagateCallbackInstance = instance;
929 m_propagateCallbackMethod = method;
930 return this;
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000931 }
Marcel Offermans706fb272010-11-15 12:52:58 +0000932
933 // TODO add equals and hashCode methods, so you can compare dependencies
Marcel Offermansa962bc92009-11-21 17:59:33 +0000934}