blob: e1e471b629c9e8368fc222d458a7660fd1cdddf7 [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 Offermans706fb272010-11-15 12:52:58 +000034import org.apache.felix.dm.Component;
Marcel Offermans9e50ef32010-10-07 11:37:00 +000035import org.apache.felix.dm.ComponentDependencyDeclaration;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000036import org.apache.felix.dm.Dependency;
Marcel Offermans3d921212010-08-09 13:37:02 +000037import org.apache.felix.dm.DependencyService;
Marcel Offermans706fb272010-11-15 12:52:58 +000038import org.apache.felix.dm.InvocationUtil;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000039import org.apache.felix.dm.ServiceDependency;
Marcel Offermans706fb272010-11-15 12:52:58 +000040import org.apache.felix.dm.ServiceUtil;
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 Offermansbde9a432010-05-11 08:01:28 +000078 private ServiceReference[] m_references;
Marcel Offermans26081d32010-07-12 12:43:42 +000079 private boolean m_propagate;
80 private Object m_propagateCallbackInstance;
81 private String m_propagateCallbackMethod;
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
106 /**
107 * Entry to wrap service properties behind a Map.
108 */
109 private final static class ServicePropertiesMapEntry implements Map.Entry {
110 private final String m_key;
111 private Object m_value;
112
113 public ServicePropertiesMapEntry(String key, Object value) {
114 m_key = key;
115 m_value = value;
116 }
117
118 public Object getKey() {
119 return m_key;
120 }
121
122 public Object getValue() {
123 return m_value;
124 }
125
126 public String toString() {
127 return m_key + "=" + m_value;
128 }
129
130 public Object setValue(Object value) {
131 Object oldValue = m_value;
132 m_value = value;
133 return oldValue;
134 }
135
136 public boolean equals(Object o) {
137 if (!(o instanceof Map.Entry)) {
138 return false;
139 }
140 Map.Entry e = (Map.Entry) o;
141 return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
142 }
143
144 public int hashCode() {
145 return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
146 }
147
148 private static final boolean eq(Object o1, Object o2) {
149 return (o1 == null ? o2 == null : o1.equals(o2));
150 }
151 }
152
153 /**
154 * Wraps service properties behind a Map.
155 */
156 private final static class ServicePropertiesMap extends AbstractMap {
157 private final ServiceReference m_ref;
158
159 public ServicePropertiesMap(ServiceReference ref) {
160 m_ref = ref;
161 }
162
163 public Object get(Object key) {
164 return m_ref.getProperty(key.toString());
165 }
166
167 public int size() {
168 return m_ref.getPropertyKeys().length;
169 }
170
171 public Set entrySet() {
172 Set set = new HashSet();
173 String[] keys = m_ref.getPropertyKeys();
174 for (int i = 0; i < keys.length; i++) {
175 set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
176 }
177 return set;
178 }
179 }
180
181 /**
182 * Creates a new service dependency.
183 *
184 * @param context the bundle context
185 * @param logger the logger
186 */
Pierre De Ropaacb3aa2009-12-04 22:53:23 +0000187 public ServiceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +0000188 super(logger);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000189 m_context = context;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000190 m_autoConfig = true;
191 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000192
Marcel Offermansb1959f42010-07-01 12:23:51 +0000193 /** Copying constructor that clones an existing instance. */
194 public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
195 super(prototype);
196 m_context = prototype.m_context;
197 m_autoConfig = prototype.m_autoConfig;
198 m_trackedServiceName = prototype.m_trackedServiceName;
199 m_nullObject = prototype.m_nullObject;
200 m_trackedServiceFilter = prototype.m_trackedServiceFilter;
201 m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
202 m_trackedServiceReference = prototype.m_trackedServiceReference;
203 m_callbackInstance = prototype.m_callbackInstance;
204 m_callbackAdded = prototype.m_callbackAdded;
205 m_callbackChanged = prototype.m_callbackChanged;
206 m_callbackRemoved = prototype.m_callbackRemoved;
207 m_autoConfigInstance = prototype.m_autoConfigInstance;
208 m_defaultImplementation = prototype.m_defaultImplementation;
209 }
210
211 public Dependency createCopy() {
212 return new ServiceDependencyImpl(this);
213 }
214
Marcel Offermansa962bc92009-11-21 17:59:33 +0000215 public synchronized boolean isAutoConfig() {
216 return m_autoConfig;
217 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000218
Marcel Offermansb196d722009-11-26 17:12:12 +0000219 public synchronized boolean isAvailable() {
220 return m_isAvailable;
221 }
222
Marcel Offermansa962bc92009-11-21 17:59:33 +0000223 public synchronized Object getService() {
224 Object service = null;
225 if (m_isStarted) {
226 service = m_tracker.getService();
227 }
Marcel Offermans001db052009-12-08 08:58:40 +0000228 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000229 service = getDefaultImplementation();
230 if (service == null) {
231 service = getNullObject();
232 }
233 }
234 return service;
235 }
236
237 public Object lookupService() {
238 Object service = null;
239 if (m_isStarted) {
Marcel Offermans001db052009-12-08 08:58:40 +0000240 service = getService();
Marcel Offermansa962bc92009-11-21 17:59:33 +0000241 }
242 else {
243 ServiceReference[] refs = null;
244 ServiceReference ref = null;
245 if (m_trackedServiceName != null) {
246 if (m_trackedServiceFilter != null) {
247 try {
248 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
249 if (refs != null) {
250 Arrays.sort(refs, COMPARATOR);
251 ref = refs[0];
252 }
253 }
254 catch (InvalidSyntaxException e) {
255 throw new IllegalStateException("Invalid filter definition for dependency.");
256 }
257 }
258 else if (m_trackedServiceReference != null) {
259 ref = m_trackedServiceReference;
260 }
261 else {
262 ref = m_context.getServiceReference(m_trackedServiceName.getName());
263 }
264 if (ref != null) {
265 service = m_context.getService(ref);
266 }
267 }
268 else {
269 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
270 }
271 }
Marcel Offermans001db052009-12-08 08:58:40 +0000272 if (service == null && isAutoConfig()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000273 service = getDefaultImplementation();
274 if (service == null) {
275 service = getNullObject();
276 }
277 }
278 return service;
279 }
Marcel Offermansb1959f42010-07-01 12:23:51 +0000280
Marcel Offermanse14b3422009-11-25 23:04:32 +0000281 // TODO lots of duplication in lookupService()
282 public ServiceReference lookupServiceReference() {
283 ServiceReference service = null;
284 if (m_isStarted) {
285 service = m_tracker.getServiceReference();
286 }
287 else {
288 ServiceReference[] refs = null;
289 ServiceReference ref = null;
290 if (m_trackedServiceName != null) {
291 if (m_trackedServiceFilter != null) {
292 try {
293 refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
294 if (refs != null) {
295 Arrays.sort(refs, COMPARATOR);
296 ref = refs[0];
297 }
298 }
299 catch (InvalidSyntaxException e) {
300 throw new IllegalStateException("Invalid filter definition for dependency.");
301 }
302 }
303 else if (m_trackedServiceReference != null) {
304 ref = m_trackedServiceReference;
305 }
306 else {
307 ref = m_context.getServiceReference(m_trackedServiceName.getName());
308 }
309 if (ref != null) {
310 service = ref;
311 }
312 }
313 else {
314 throw new IllegalStateException("Could not lookup dependency, no service name specified.");
315 }
316 }
317 return service;
318 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000319
320 private Object getNullObject() {
321 if (m_nullObject == null) {
322 Class trackedServiceName;
323 synchronized (this) {
324 trackedServiceName = m_trackedServiceName;
325 }
326 try {
327 m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
328 }
329 catch (Exception e) {
330 m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
331 }
332 }
333 return m_nullObject;
334 }
335
336 private Object getDefaultImplementation() {
337 if (m_defaultImplementation != null) {
338 if (m_defaultImplementation instanceof Class) {
339 try {
340 m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
341 }
342 catch (Exception e) {
343 m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
344 }
345 }
346 else {
347 m_defaultImplementationInstance = m_defaultImplementation;
348 }
349 }
350 return m_defaultImplementationInstance;
351 }
352
353 public synchronized Class getInterface() {
354 return m_trackedServiceName;
355 }
356
Marcel Offermanse14b3422009-11-25 23:04:32 +0000357 public void start(DependencyService service) {
358 boolean needsStarting = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000359 synchronized (this) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000360 m_services.add(service);
361 if (!m_isStarted) {
362 if (m_trackedServiceName != null) {
363 if (m_trackedServiceFilter != null) {
364 try {
365 m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
366 }
367 catch (InvalidSyntaxException e) {
Marcel Offermansad760672010-03-03 15:30:01 +0000368 throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000369 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000370 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000371 else if (m_trackedServiceReference != null) {
372 m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000373 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000374 else {
375 m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
376 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000377 }
378 else {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000379 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000380 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000381 m_isStarted = true;
382 needsStarting = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000383 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000384 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000385 if (needsStarting) {
386 m_tracker.open();
387 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000388 }
389
Marcel Offermanse14b3422009-11-25 23:04:32 +0000390 public void stop(DependencyService service) {
391 boolean needsStopping = false;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000392 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000393 if (m_services.size() == 1 && m_services.contains(service)) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000394 m_isStarted = false;
395 needsStopping = true;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000396 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000397 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000398 if (needsStopping) {
399 m_tracker.close();
400 m_tracker = null;
401 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000402 //moved this down
403 synchronized (this) {
404 m_services.remove(service);
405 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000406 }
407
408 public Object addingService(ServiceReference ref) {
409 Object service = m_context.getService(ref);
410 // first check to make sure the service is actually an instance of our service
411 if (!m_trackedServiceName.isInstance(service)) {
412 return null;
413 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000414 return service;
415 }
416
417 public void addedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000418 boolean makeAvailable = makeAvailable();
419
Marcel Offermans203bdad2009-12-04 09:23:04 +0000420 Object[] services;
421 synchronized (this) {
422 services = m_services.toArray();
423 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000424 for (int i = 0; i < services.length; i++) {
425 DependencyService ds = (DependencyService) services[i];
426 if (makeAvailable) {
Marcel Offermansfea14842010-10-14 14:45:36 +0000427 if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
428 invokeAdded(ds, ref, service);
429 }
Pierre De Rop1785c332010-06-11 06:03:53 +0000430 // The dependency callback will be defered until all required dependency are available.
431 ds.dependencyAvailable(this);
432 if (!isRequired()) {
433 // For optional dependency, we always invoke callback, because at this point, we know
434 // that the service has been started, and the service start method has been called.
435 // (See the ServiceImpl.bindService method, which will activate optional dependencies using
436 // startTrackingOptional() method).
Marcel Offermanse14b3422009-11-25 23:04:32 +0000437 invokeAdded(ds, ref, service);
438 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000439 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000440 else {
441 ds.dependencyChanged(this);
Pierre De Rop1785c332010-06-11 06:03:53 +0000442 // At this point, either the dependency is optional (meaning that the service has been started,
443 // because if not, then our dependency would not be active); or the dependency is required,
444 // meaning that either the service is not yet started, or already started.
445 // In all cases, we have to inject the required dependency.
Marcel Offermanse14b3422009-11-25 23:04:32 +0000446 invokeAdded(ds, ref, service);
447 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000448 }
449 }
450
Marcel Offermansa962bc92009-11-21 17:59:33 +0000451 public void modifiedService(ServiceReference ref, Object service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000452 Object[] services;
453 synchronized (this) {
454 services = m_services.toArray();
455 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000456 for (int i = 0; i < services.length; i++) {
457 DependencyService ds = (DependencyService) services[i];
458 ds.dependencyChanged(this);
459 if (ds.isRegistered()) {
460 invokeChanged(ds, ref, service);
461 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000462 }
463 }
464
Marcel Offermansa962bc92009-11-21 17:59:33 +0000465 public void removedService(ServiceReference ref, Object service) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000466 boolean makeUnavailable = makeUnavailable();
467
Marcel Offermans706fb272010-11-15 12:52:58 +0000468 System.out.println("removedService: " + makeUnavailable + " for " + ServiceUtil.toString(ref));
469
Marcel Offermans203bdad2009-12-04 09:23:04 +0000470 Object[] services;
471 synchronized (this) {
472 services = m_services.toArray();
473 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000474
Marcel Offermanse14b3422009-11-25 23:04:32 +0000475 for (int i = 0; i < services.length; i++) {
476 DependencyService ds = (DependencyService) services[i];
477 if (makeUnavailable) {
478 ds.dependencyUnavailable(this);
Marcel Offermansfea14842010-10-14 14:45:36 +0000479 if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000480 invokeRemoved(ds, ref, service);
481 }
482 }
483 else {
484 ds.dependencyChanged(this);
485 invokeRemoved(ds, ref, service);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000486 }
487 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000488 // unget what we got in addingService (see ServiceTracker 701.4.1)
489 m_context.ungetService(ref);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000490
Marcel Offermansa962bc92009-11-21 17:59:33 +0000491 }
492
Marcel Offermansea89b862010-06-24 13:14:43 +0000493 public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
494 invoke(dependencyService, reference, service, m_callbackAdded);
Marcel Offermans706fb272010-11-15 12:52:58 +0000495 //marrs
496 m_refs.add(reference);
Marcel Offermansea89b862010-06-24 13:14:43 +0000497 }
498
499 public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
500 invoke(dependencyService, reference, service, m_callbackChanged);
501 }
502
Marcel Offermansb196d722009-11-26 17:12:12 +0000503 public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000504 invoke(dependencyService, reference, service, m_callbackRemoved);
Marcel Offermans706fb272010-11-15 12:52:58 +0000505 //marrs
506 m_refs.remove(reference);
Marcel Offermansea89b862010-06-24 13:14:43 +0000507 }
508
509 public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
510 if (name != null) {
511 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
Marcel Offermans706fb272010-11-15 12:52:58 +0000512 new Class[][] {
513 {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},
514 {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
515 },
516 new Object[][] {
517 {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
518 {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
519 }
Marcel Offermansb196d722009-11-26 17:12:12 +0000520 );
Marcel Offermansa962bc92009-11-21 17:59:33 +0000521 }
522 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000523
Marcel Offermans74363c32009-11-23 19:56:08 +0000524 protected synchronized boolean makeAvailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000525 if (!isAvailable()) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000526 m_isAvailable = true;
527 return true;
528 }
529 return false;
530 }
531
532 private synchronized boolean makeUnavailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000533 if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000534 m_isAvailable = false;
535 return true;
536 }
537 return false;
538 }
539
Marcel Offermanse14b3422009-11-25 23:04:32 +0000540 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
541 if (m_callbackInstance == null) {
542 return dependencyService.getCompositionInstances();
543 }
544 else {
545 return new Object[] { m_callbackInstance };
546 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000547 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000548
549 // ----- CREATION
550
551 /**
552 * Sets the name of the service that should be tracked.
553 *
554 * @param serviceName the name of the service
555 * @return this service dependency
556 */
557 public synchronized ServiceDependency setService(Class serviceName) {
558 ensureNotActive();
559 if (serviceName == null) {
560 throw new IllegalArgumentException("Service name cannot be null.");
561 }
562 m_trackedServiceName = serviceName;
563 m_trackedServiceReference = null;
564 m_trackedServiceFilter = null;
565 return this;
566 }
567
568 /**
569 * Sets the name of the service that should be tracked. You can either specify
570 * only the name, or the name and a filter. In the latter case, the filter is used
571 * to track the service and should only return services of the type that was specified
572 * in the name. To make sure of this, the filter is actually extended internally to
573 * filter on the correct name.
574 *
575 * @param serviceName the name of the service
576 * @param serviceFilter the filter condition
577 * @return this service dependency
578 */
579 public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
580 ensureNotActive();
581 if (serviceName == null) {
582 throw new IllegalArgumentException("Service name cannot be null.");
583 }
584 m_trackedServiceName = serviceName;
585 if (serviceFilter != null) {
586 m_trackedServiceFilterUnmodified = serviceFilter;
587 m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
588 }
589 else {
590 m_trackedServiceFilterUnmodified = null;
591 m_trackedServiceFilter = null;
592 }
593 m_trackedServiceReference = null;
594 return this;
595 }
Marcel Offermansa83c25a2009-12-22 13:38:38 +0000596
597 public synchronized ServiceDependency setService(String serviceFilter) {
598 ensureNotActive();
599 if (serviceFilter == null) {
600 throw new IllegalArgumentException("Service filter cannot be null.");
601 }
602 m_trackedServiceName = Object.class;
603 if (serviceFilter != null) {
604 m_trackedServiceFilterUnmodified = serviceFilter;
605 m_trackedServiceFilter = serviceFilter;
606 }
607 m_trackedServiceReference = null;
608 return this;
609 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000610
611 /**
612 * Sets the name of the service that should be tracked. You can either specify
613 * only the name, or the name and a reference. In the latter case, the service reference
614 * is used to track the service and should only return services of the type that was
615 * specified in the name.
616 *
617 * @param serviceName the name of the service
618 * @param serviceReference the service reference to track
619 * @return this service dependency
620 */
621 public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
622 ensureNotActive();
623 if (serviceName == null) {
624 throw new IllegalArgumentException("Service name cannot be null.");
625 }
626 m_trackedServiceName = serviceName;
627 m_trackedServiceReference = serviceReference;
628 m_trackedServiceFilterUnmodified = null;
629 m_trackedServiceFilter = null;
630 return this;
631 }
632
633 /**
634 * Sets the default implementation for this service dependency. You can use this to supply
635 * your own implementation that will be used instead of a Null Object when the dependency is
636 * not available. This is also convenient if the service dependency is not an interface
637 * (which would cause the Null Object creation to fail) but a class.
638 *
639 * @param implementation the instance to use or the class to instantiate if you want to lazily
640 * instantiate this implementation
641 * @return this service dependency
642 */
643 public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
644 ensureNotActive();
645 m_defaultImplementation = implementation;
646 return this;
647 }
648
649 /**
650 * Sets the required flag which determines if this service is required or not.
651 *
652 * @param required the required flag
653 * @return this service dependency
654 */
655 public synchronized ServiceDependency setRequired(boolean required) {
656 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000657 setIsRequired(required);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000658 return this;
659 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000660
661 public ServiceDependency setInstanceBound(boolean isInstanceBound) {
Marcel Offermans61a81142010-04-02 15:16:50 +0000662 setIsInstanceBound(isInstanceBound);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000663 return this;
664 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000665
666 /**
667 * Sets auto configuration for this service. Auto configuration allows the
668 * dependency to fill in any attributes in the service implementation that
669 * are of the same type as this dependency. Default is on.
670 *
671 * @param autoConfig the value of auto config
672 * @return this service dependency
673 */
674 public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
675 ensureNotActive();
676 m_autoConfig = autoConfig;
677 m_autoConfigInvoked = true;
678 return this;
679 }
680
681 /**
682 * Sets auto configuration for this service. Auto configuration allows the
683 * dependency to fill in the attribute in the service implementation that
684 * has the same type and instance name.
685 *
686 * @param instanceName the name of attribute to auto config
687 * @return this service dependency
688 */
689 public synchronized ServiceDependency setAutoConfig(String instanceName) {
690 ensureNotActive();
691 m_autoConfig = (instanceName != null);
692 m_autoConfigInstance = instanceName;
693 m_autoConfigInvoked = true;
694 return this;
695 }
696
697 /**
698 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
699 * dependency is added or removed. When you specify callbacks, the auto configuration
700 * feature is automatically turned off, because we're assuming you don't need it in this
701 * case.
702 *
703 * @param added the method to call when a service was added
704 * @param removed the method to call when a service was removed
705 * @return this service dependency
706 */
707 public synchronized ServiceDependency setCallbacks(String added, String removed) {
708 return setCallbacks(null, added, null, removed);
709 }
710
711 /**
712 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
713 * dependency is added, changed or removed. When you specify callbacks, the auto
714 * configuration feature is automatically turned off, because we're assuming you don't
715 * need it in this case.
716 *
717 * @param added the method to call when a service was added
718 * @param changed the method to call when a service was changed
719 * @param removed the method to call when a service was removed
720 * @return this service dependency
721 */
722 public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
723 return setCallbacks(null, added, changed, removed);
724 }
725
726 /**
727 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
728 * dependency is added or removed. They are called on the instance you provide. When you
729 * specify callbacks, the auto configuration feature is automatically turned off, because
730 * we're assuming you don't need it in this case.
731 *
732 * @param instance the instance to call the callbacks on
733 * @param added the method to call when a service was added
734 * @param removed the method to call when a service was removed
735 * @return this service dependency
736 */
737 public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
738 return setCallbacks(instance, added, null, removed);
739 }
740
741 /**
742 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
743 * dependency is added, changed or removed. They are called on the instance you provide. When you
744 * specify callbacks, the auto configuration feature is automatically turned off, because
745 * we're assuming you don't need it in this case.
746 *
747 * @param instance the instance to call the callbacks on
748 * @param added the method to call when a service was added
749 * @param changed the method to call when a service was changed
750 * @param removed the method to call when a service was removed
751 * @return this service dependency
752 */
753 public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
754 ensureNotActive();
755 // if at least one valid callback is specified, we turn off auto configuration, unless
756 // someone already explicitly invoked autoConfig
757 if ((added != null || removed != null || changed != null) && ! m_autoConfigInvoked) {
758 setAutoConfig(false);
759 }
760 m_callbackInstance = instance;
761 m_callbackAdded = added;
762 m_callbackChanged = changed;
763 m_callbackRemoved = removed;
764 return this;
765 }
766
767 private void ensureNotActive() {
768 if (m_tracker != null) {
769 throw new IllegalStateException("Cannot modify state while active.");
770 }
771 }
772
773 public synchronized String toString() {
774 return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
775 }
776
777 public String getAutoConfigName() {
778 return m_autoConfigInstance;
779 }
Marcel Offermans001db052009-12-08 08:58:40 +0000780
781 public Object getAutoConfigInstance() {
782 return lookupService();
783 }
784
785 public Class getAutoConfigType() {
786 return getInterface();
787 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000788
789 public String getName() {
790 StringBuilder sb = new StringBuilder();
Marcel Offermansb1959f42010-07-01 12:23:51 +0000791 if (m_trackedServiceName != null) {
792 sb.append(m_trackedServiceName.getName());
793 if (m_trackedServiceFilterUnmodified != null) {
794 sb.append(' ');
795 sb.append(m_trackedServiceFilterUnmodified);
796 }
797 }
798 if (m_trackedServiceReference != null) {
Marcel Offermans9e50ef32010-10-07 11:37:00 +0000799 sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
Marcel Offermansa962bc92009-11-21 17:59:33 +0000800 }
801 return sb.toString();
802 }
803
804 public int getState() {
805 return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
806 }
807
808 public String getType() {
809 return "service";
810 }
Marcel Offermans001db052009-12-08 08:58:40 +0000811
Marcel Offermans706fb272010-11-15 12:52:58 +0000812 private List m_refs = new ArrayList();
813
Marcel Offermans001db052009-12-08 08:58:40 +0000814 public void invokeAdded(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +0000815 ServiceReference[] refs = m_tracker.getServiceReferences();
816 if (refs != null) {
817 for (int i = 0; i < refs.length; i++) {
818 ServiceReference sr = refs[i];
819 Object svc = m_context.getService(sr);
Marcel Offermans706fb272010-11-15 12:52:58 +0000820 System.out.println("invokeAdded " + i + " " + ServiceUtil.toString(sr));
Marcel Offermansbde9a432010-05-11 08:01:28 +0000821 invokeAdded(service, sr, svc);
Marcel Offermans706fb272010-11-15 12:52:58 +0000822
823 //marrs
824 m_refs.add(sr);
Marcel Offermansbde9a432010-05-11 08:01:28 +0000825 }
826 }
827 m_references = refs;
Marcel Offermans706fb272010-11-15 12:52:58 +0000828
Marcel Offermans001db052009-12-08 08:58:40 +0000829 }
Marcel Offermansbde9a432010-05-11 08:01:28 +0000830
Marcel Offermans001db052009-12-08 08:58:40 +0000831 public void invokeRemoved(DependencyService service) {
Marcel Offermansbde9a432010-05-11 08:01:28 +0000832 ServiceReference[] refs = m_references;
Marcel Offermans706fb272010-11-15 12:52:58 +0000833
834
Marcel Offermansbde9a432010-05-11 08:01:28 +0000835 if (refs != null) {
Marcel Offermans706fb272010-11-15 12:52:58 +0000836 //marrs
837 refs = (ServiceReference[]) m_refs.toArray(refs);
838
Marcel Offermansbde9a432010-05-11 08:01:28 +0000839 for (int i = 0; i < refs.length; i++) {
840 ServiceReference sr = refs[i];
841 Object svc = m_context.getService(sr);
Marcel Offermans706fb272010-11-15 12:52:58 +0000842 System.out.println("invokeRemoved " + i + " " + ServiceUtil.toString(sr));
843 if (sr.getBundle() == null) {
844 System.out.println("invokeRemoved OLD SHIT .. SKIPPING .. not");
845// break;
846 }
Marcel Offermansbde9a432010-05-11 08:01:28 +0000847 invokeRemoved(service, sr, svc);
848 }
849 }
850 m_references = null;
Marcel Offermans706fb272010-11-15 12:52:58 +0000851
852 //marrs
853 m_refs.clear();
Marcel Offermans001db052009-12-08 08:58:40 +0000854 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000855
856 public Dictionary getProperties() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000857 ServiceReference reference = lookupServiceReference();
858 Object service = lookupService();
859 if (reference != null) {
860 if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
861 try {
862 return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
863 }
864 catch (InvocationTargetException e) {
865 m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
866 }
867 catch (Exception e) {
868 m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
869 }
870 throw new IllegalStateException("Could not invoke callback");
871 }
872 else {
873 Properties props = new Properties();
874 String[] keys = reference.getPropertyKeys();
875 for (int i = 0; i < keys.length; i++) {
876 if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
877 props.put(keys[i], reference.getProperty(keys[i]));
878 }
879 }
880 return props;
881 }
882 }
883 else {
884 throw new IllegalStateException("cannot find service reference");
885 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000886 }
887
888 public boolean isPropagated() {
Marcel Offermans26081d32010-07-12 12:43:42 +0000889 return m_propagate;
890 }
891
892 public ServiceDependency setPropagate(boolean propagate) {
893 ensureNotActive();
894 m_propagate = propagate;
895 return this;
896 }
897
898 public ServiceDependency setPropagate(Object instance, String method) {
899 setPropagate(instance != null && method != null);
900 m_propagateCallbackInstance = instance;
901 m_propagateCallbackMethod = method;
902 return this;
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000903 }
Marcel Offermans706fb272010-11-15 12:52:58 +0000904
905 // TODO add equals and hashCode methods, so you can compare dependencies
Marcel Offermansa962bc92009-11-21 17:59:33 +0000906}