blob: 943967e1e61cd0f883083c8e09ee521b33c93729 [file] [log] [blame]
Marcel Offermans516d38d2006-03-25 20:46:19 +00001/*
2 * Copyright 2006 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package org.apache.felix.dependencymanager;
18
19import java.lang.reflect.AccessibleObject;
20import java.lang.reflect.Field;
21import java.lang.reflect.Proxy;
22import java.util.ArrayList;
23import java.util.Dictionary;
24import java.util.Hashtable;
25import java.util.Iterator;
26import java.util.List;
27
28import org.osgi.framework.BundleContext;
29import org.osgi.framework.ServiceRegistration;
30
31/**
32 * Service implementation.
33 *
34 * @author Marcel Offermans
35 */
36public class ServiceImpl implements Service {
37 private static final ServiceRegistration NULL_REGISTRATION;
38 private static final int STARTING = 1;
39 private static final int WAITING_FOR_REQUIRED = 2;
40 private static final int TRACKING_OPTIONAL = 3;
41 private static final int STOPPING = 4;
42 private static final String[] STATE_NAMES = {
43 "(unknown)",
44 "starting",
45 "waiting for required dependencies",
46 "tracking optional dependencies",
47 "stopping"};
48
49 private BundleContext m_context;
50 private ServiceRegistration m_registration;
51
52 private String m_callbackInit;
53 private String m_callbackStart;
54 private String m_callbackStop;
55 private String m_callbackDestroy;
56
57 private List m_listeners = new ArrayList();
58 private ArrayList m_dependencies = new ArrayList();
59 private int m_state;
60
61 private Object m_serviceInstance;
62 private Object m_implementation;
63 private Object m_serviceName;
64 private Dictionary m_serviceProperties;
65
66 public ServiceImpl(BundleContext context) {
67 m_state = STARTING;
68 m_context = context;
69 m_callbackInit = "init";
70 m_callbackStart = "start";
71 m_callbackStop = "stop";
72 m_callbackDestroy = "destroy";
73 }
74
75 public Service add(Dependency dependency) {
76 synchronized (m_dependencies) {
77 m_dependencies.add(dependency);
78 }
79 if (m_state == WAITING_FOR_REQUIRED) {
80 // if we're waiting for required dependencies, and
81 // this is a required dependency, start tracking it
82 // ...otherwise, we don't need to do anything yet
83 if (dependency.isRequired()) {
84 dependency.start(this);
85 }
86 }
87 else if (m_state == TRACKING_OPTIONAL) {
88 // start tracking the dependency
89 dependency.start(this);
90 if (dependency.isRequired() && !dependency.isAvailable()) {
91 // if this is a required dependency and it can not
92 // be resolved right away, then we need to go back to
93 // the waiting for required state, until this
94 // dependency is available
95 deactivateService();
96 }
97 }
98 return this;
99 }
100
101 public Service remove(Dependency dependency) {
102 synchronized (m_dependencies) {
103 m_dependencies.remove(dependency);
104 }
105 if (m_state == TRACKING_OPTIONAL) {
106 // if we're tracking optional dependencies, then any
107 // dependency that is removed can be stopped without
108 // causing state changes
109 dependency.stop(this);
110 }
111 else if (m_state == WAITING_FOR_REQUIRED) {
112 // if we're waiting for required dependencies, then
113 // we only need to stop tracking the dependency if it
114 // too is required; this might trigger a state change
Marcel Offermansda3bf6e2006-04-05 14:57:49 +0000115 if (dependency.isRequired()) {
116 dependency.stop(this);
117 if (allRequiredDependenciesAvailable()) {
118 activateService();
119 }
Marcel Offermans516d38d2006-03-25 20:46:19 +0000120 }
121 }
122 return this;
123 }
124
125 public List getDependencies() {
126 List list;
127 synchronized (m_dependencies) {
128 list = (List) m_dependencies.clone();
129 }
130 return list;
131 }
132
133 public ServiceRegistration getServiceRegistration() {
134 return m_registration;
135 }
136
137 public Object getService() {
138 return m_serviceInstance;
139 }
140
141 public void dependencyAvailable(Dependency dependency) {
142 if ((dependency.isRequired())
143 && (m_state == WAITING_FOR_REQUIRED)
144 && (allRequiredDependenciesAvailable())) {
145 activateService();
146 }
147 if ((!dependency.isRequired()) && (m_state == TRACKING_OPTIONAL)) {
148 updateInstance(dependency);
149 }
150 }
151
152 public void dependencyChanged(Dependency dependency) {
153 if (m_state == TRACKING_OPTIONAL) {
154 updateInstance(dependency);
155 }
156 }
157
158 public void dependencyUnavailable(Dependency dependency) {
159 if (dependency.isRequired()) {
160 if (m_state == TRACKING_OPTIONAL) {
161 if (!allRequiredDependenciesAvailable()) {
162 deactivateService();
163 }
164 }
165 }
166 else {
167 // optional dependency
168 }
169 if (m_state == TRACKING_OPTIONAL) {
170 updateInstance(dependency);
171 }
172 }
173
174 public synchronized void start() {
175 if ((m_state != STARTING) && (m_state != STOPPING)) {
176 throw new IllegalStateException("Cannot start from state " + STATE_NAMES[m_state]);
177 }
178 startTrackingRequired();
179 if (allRequiredDependenciesAvailable() && (m_state == WAITING_FOR_REQUIRED)) {
180 activateService();
181 }
182 }
183
184 public synchronized void stop() {
185 if ((m_state != WAITING_FOR_REQUIRED) && (m_state != TRACKING_OPTIONAL)) {
186 if ((m_state > 0) && (m_state < STATE_NAMES.length)) {
187 throw new IllegalStateException("Cannot stop from state " + STATE_NAMES[m_state]);
188 }
189 throw new IllegalStateException("Cannot stop from unknown state.");
190 }
191 if (m_state == TRACKING_OPTIONAL) {
192 deactivateService();
193 }
194 stopTrackingRequired();
195 }
196
197 private void activateService() {
198 // service activation logic, first we initialize the service instance itself
199 // meaning it is created if necessary and the bundle context is set
200 initService();
201 // then we invoke the init callback so the service can further initialize
202 // itself
203 invoke(m_callbackInit);
204 // now is the time to configure the service, meaning all required
205 // dependencies will be set and any callbacks called
206 configureService();
207 // inform the state listeners we're starting
208 stateListenersStarting();
209 // start tracking optional services
210 startTrackingOptional();
211 // invoke the start callback, since we're now ready to be used
212 invoke(m_callbackStart);
213 // register the service in the framework's service registry
214 registerService();
215 // inform the state listeners we've started
216 stateListenersStarted();
217 }
218
219 private void deactivateService() {
220 // service deactivation logic, first inform the state listeners
221 // we're stopping
222 stateListenersStopping();
223 // then, unregister the service from the framework
224 unregisterService();
225 // invoke the stop callback
226 invoke(m_callbackStop);
227 // stop tracking optional services
228 stopTrackingOptional();
229 // inform the state listeners we've stopped
230 stateListenersStopped();
231 // invoke the destroy callback
232 invoke(m_callbackDestroy);
233 // destroy the service instance
234 destroyService();
235 }
236
237 private void invoke(String name) {
238 if (name != null) {
239 // invoke method if it exists
240 AccessibleObject.setAccessible(m_serviceInstance.getClass().getDeclaredMethods(), true);
241 try {
242 m_serviceInstance.getClass().getDeclaredMethod(name, null).invoke(m_serviceInstance, null);
243 }
244 catch (NoSuchMethodException e) {
245 // ignore this, we don't care if the method does not exist
246 }
247 catch (Exception e) {
248 // TODO handle this exception
249 e.printStackTrace();
250 }
251 }
252 }
253
254 private synchronized void stateListenersStarting() {
255 Iterator i = m_listeners.iterator();
256 while (i.hasNext()) {
257 ServiceStateListener ssl = (ServiceStateListener) i.next();
258 ssl.starting(this);
259 }
260 }
261
262 private synchronized void stateListenersStarted() {
263 Iterator i = m_listeners.iterator();
264 while (i.hasNext()) {
265 ServiceStateListener ssl = (ServiceStateListener) i.next();
266 ssl.started(this);
267 }
268 }
269
270 private synchronized void stateListenersStopping() {
271 Iterator i = m_listeners.iterator();
272 while (i.hasNext()) {
273 ServiceStateListener ssl = (ServiceStateListener) i.next();
274 ssl.stopping(this);
275 }
276 }
277
278 private synchronized void stateListenersStopped() {
279 Iterator i = m_listeners.iterator();
280 while (i.hasNext()) {
281 ServiceStateListener ssl = (ServiceStateListener) i.next();
282 ssl.stopped(this);
283 }
284 }
285
286 private boolean allRequiredDependenciesAvailable() {
287 Iterator i = getDependencies().iterator();
288 while (i.hasNext()) {
289 Dependency dependency = (Dependency) i.next();
290 if (dependency.isRequired() && !dependency.isAvailable()) {
291 return false;
292 }
293 }
294 return true;
295 }
296
297 private void startTrackingOptional() {
298 m_state = TRACKING_OPTIONAL;
299 Iterator i = getDependencies().iterator();
300 while (i.hasNext()) {
301 Dependency dependency = (Dependency) i.next();
302 if (!dependency.isRequired()) {
303 dependency.start(this);
304 }
305 }
306 }
307
308 private void stopTrackingOptional() {
309 m_state = WAITING_FOR_REQUIRED;
310 Iterator i = getDependencies().iterator();
311 while (i.hasNext()) {
312 Dependency dependency = (Dependency) i.next();
313 if (!dependency.isRequired()) {
314 dependency.stop(this);
315 }
316 }
317 }
318
319 private void startTrackingRequired() {
320 m_state = WAITING_FOR_REQUIRED;
321 Iterator i = getDependencies().iterator();
322 while (i.hasNext()) {
323 Dependency dependency = (Dependency) i.next();
324 if (dependency.isRequired()) {
325 dependency.start(this);
326 }
327 }
328 }
329
330 private void stopTrackingRequired() {
331 m_state = STOPPING;
332 Iterator i = getDependencies().iterator();
333 while (i.hasNext()) {
334 Dependency dependency = (Dependency) i.next();
335 if (dependency.isRequired()) {
336 dependency.stop(this);
337 }
338 }
339 }
340
341 private void initService() {
342 if (m_implementation instanceof Class) {
343 // instantiate
344 try {
345 m_serviceInstance = ((Class) m_implementation).newInstance();
346 } catch (InstantiationException e) {
347 // TODO handle this exception
348 e.printStackTrace();
349 } catch (IllegalAccessException e) {
350 // TODO handle this exception
351 e.printStackTrace();
352 }
353 }
354 else {
355 m_serviceInstance = m_implementation;
356 }
357 // configure the bundle context
358 configureImplementation(BundleContext.class, m_context);
359 configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
360
361 }
362
363 private void configureService() {
364 // configure all services (the optional dependencies might be configured
365 // as null objects but that's what we want at this point)
366 configureServices();
367 }
368
369 private void destroyService() {
370 unconfigureServices();
371 m_serviceInstance = null;
372 }
373
374 private void registerService() {
375 if (m_serviceName != null) {
376 ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
377 m_registration = wrapper;
378 configureImplementation(ServiceRegistration.class, wrapper);
379 // service name can either be a string or an array of strings
380 ServiceRegistration registration;
381 if (m_serviceName instanceof String) {
382 registration = m_context.registerService((String) m_serviceName, m_serviceInstance, m_serviceProperties);
383 }
384 else {
385 registration = m_context.registerService((String[]) m_serviceName, m_serviceInstance, m_serviceProperties);
386 }
387 wrapper.setServiceRegistration(registration);
388 }
389 }
390
391 private void unregisterService() {
392 if (m_serviceName != null) {
393 m_registration.unregister();
394 configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
395 }
396 }
397
398 private void updateInstance(Dependency dependency) {
399 if (dependency instanceof ServiceDependency) {
400 ServiceDependency sd = (ServiceDependency) dependency;
401 // update the dependency in the service instance (it will use
402 // a null object if necessary)
403 if (sd.isAutoConfig()) {
404 configureImplementation(sd.getInterface(), sd.getService());
405 }
406 }
407 }
408
409 /**
410 * Configure a field in the service implementation. The service implementation
411 * is searched for fields that have the same type as the class that was specified
412 * and for each of these fields, the specified instance is filled in.
413 *
414 * @param clazz the class to search for
415 * @param instance the instance to fill in
416 */
417 private void configureImplementation(Class clazz, Object instance) {
418 Class serviceClazz = m_serviceInstance.getClass();
419 while (serviceClazz != null) {
420 Field[] fields = serviceClazz.getDeclaredFields();
421 AccessibleObject.setAccessible(fields, true);
422 for (int j = 0; j < fields.length; j++) {
423 if (fields[j].getType().equals(clazz)) {
424 try {
425 // synchronized makes sure the field is actually written to immediately
426 synchronized (new Object()) {
427 fields[j].set(m_serviceInstance, instance);
428 }
429 }
430 catch (Exception e) {
431 System.err.println("Exception while trying to set " + fields[j].getName() +
432 " of type " + fields[j].getType().getName() +
433 " by classloader " + fields[j].getType().getClassLoader() +
434 " which should equal type " + clazz.getName() +
435 " by classloader " + clazz.getClassLoader() +
436 " of type " + serviceClazz.getName() +
437 " by classloader " + serviceClazz.getClassLoader() +
438 " on " + m_serviceInstance +
439 " by classloader " + m_serviceInstance.getClass().getClassLoader() +
440 "\nDumping stack:"
441 );
442 e.printStackTrace();
443 System.out.println("C: " + clazz);
444 System.out.println("I: " + instance);
445 System.out.println("I:C: " + instance.getClass().getClassLoader());
446 Class[] classes = instance.getClass().getInterfaces();
447 for (int i = 0; i < classes.length; i++) {
448 Class c = classes[i];
449 System.out.println("I:C:I: " + c);
450 System.out.println("I:C:I:C: " + c.getClassLoader());
451 }
452 System.out.println("F: " + fields[j]);
453 throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + m_serviceInstance);
454 }
455 }
456 }
457 serviceClazz = serviceClazz.getSuperclass();
458 }
459 }
460
461 private void configureServices() {
462 Iterator i = getDependencies().iterator();
463 while (i.hasNext()) {
464 Dependency dependency = (Dependency) i.next();
465 if (dependency instanceof ServiceDependency) {
466 ServiceDependency sd = (ServiceDependency) dependency;
467 if (sd.isAutoConfig()) {
468 configureImplementation(sd.getInterface(), sd.getService());
469 }
470 // for required dependencies, we invoke any callbacks here
471 if (sd.isRequired()) {
472 sd.invokeAdded();
473 }
474 }
475 }
476 }
477
478 private void unconfigureServices() {
479 Iterator i = getDependencies().iterator();
480 while (i.hasNext()) {
481 Dependency dependency = (Dependency) i.next();
482 if (dependency instanceof ServiceDependency) {
483 ServiceDependency sd = (ServiceDependency) dependency;
484 // for required dependencies, we invoke any callbacks here
485 if (sd.isRequired()) {
486 sd.invokeRemoved();
487 }
488 }
489 }
490 }
491
492 public synchronized void addStateListener(ServiceStateListener listener) {
493 m_listeners.add(listener);
494 if (m_state == TRACKING_OPTIONAL) {
495 listener.starting(this);
496 listener.started(this);
497 }
498 }
499
500 public synchronized void removeStateListener(ServiceStateListener listener) {
501 m_listeners.remove(listener);
502 }
503
504 synchronized void removeStateListeners() {
505 m_listeners.clear();
506 }
507
508 public synchronized Service setInterface(String serviceName, Dictionary properties) {
509 ensureNotActive();
510 m_serviceName = serviceName;
511 m_serviceProperties = properties;
512 return this;
513 }
514
515 public synchronized Service setInterface(String[] serviceName, Dictionary properties) {
516 ensureNotActive();
517 m_serviceName = serviceName;
518 m_serviceProperties = properties;
519 return this;
520 }
521
522 public synchronized Service setCallbacks(String init, String start, String stop, String destroy) {
523 ensureNotActive();
524 m_callbackInit = init;
525 m_callbackStart = start;
526 m_callbackStop = stop;
527 m_callbackDestroy = destroy;
528 return this;
529 }
530
531 public synchronized Service setImplementation(Object implementation) {
532 ensureNotActive();
533 m_implementation = implementation;
534 return this;
535 }
536
537 private void ensureNotActive() {
538 if ((m_state == TRACKING_OPTIONAL) || (m_state == WAITING_FOR_REQUIRED)) {
539 throw new IllegalStateException("Cannot modify state while active.");
540 }
541 }
542 boolean isRegistered() {
543 return (m_state == TRACKING_OPTIONAL);
544 }
545
546 public String toString() {
547 return "ServiceImpl[" + m_serviceName + " " + m_implementation + "]";
548 }
549
550 public synchronized Dictionary getServiceProperties() {
551 if (m_serviceProperties != null) {
552 return (Dictionary) ((Hashtable) m_serviceProperties).clone();
553 }
554 return null;
555 }
556
557 public synchronized void setServiceProperties(Dictionary serviceProperties) {
558 m_serviceProperties = serviceProperties;
559 if (isRegistered() && (m_serviceName != null) && (m_serviceProperties != null)) {
560 m_registration.setProperties(m_serviceProperties);
561 }
562 }
563
564 static {
565 NULL_REGISTRATION = (ServiceRegistration) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[] {ServiceRegistration.class}, new DefaultNullObject());
566 }
567}