blob: 26a6a4ff0bde725ffd2ba6803fd695fceeb513c8 [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
115 dependency.stop(this);
116 if (allRequiredDependenciesAvailable()) {
117 activateService();
118 }
119 }
120 return this;
121 }
122
123 public List getDependencies() {
124 List list;
125 synchronized (m_dependencies) {
126 list = (List) m_dependencies.clone();
127 }
128 return list;
129 }
130
131 public ServiceRegistration getServiceRegistration() {
132 return m_registration;
133 }
134
135 public Object getService() {
136 return m_serviceInstance;
137 }
138
139 public void dependencyAvailable(Dependency dependency) {
140 if ((dependency.isRequired())
141 && (m_state == WAITING_FOR_REQUIRED)
142 && (allRequiredDependenciesAvailable())) {
143 activateService();
144 }
145 if ((!dependency.isRequired()) && (m_state == TRACKING_OPTIONAL)) {
146 updateInstance(dependency);
147 }
148 }
149
150 public void dependencyChanged(Dependency dependency) {
151 if (m_state == TRACKING_OPTIONAL) {
152 updateInstance(dependency);
153 }
154 }
155
156 public void dependencyUnavailable(Dependency dependency) {
157 if (dependency.isRequired()) {
158 if (m_state == TRACKING_OPTIONAL) {
159 if (!allRequiredDependenciesAvailable()) {
160 deactivateService();
161 }
162 }
163 }
164 else {
165 // optional dependency
166 }
167 if (m_state == TRACKING_OPTIONAL) {
168 updateInstance(dependency);
169 }
170 }
171
172 public synchronized void start() {
173 if ((m_state != STARTING) && (m_state != STOPPING)) {
174 throw new IllegalStateException("Cannot start from state " + STATE_NAMES[m_state]);
175 }
176 startTrackingRequired();
177 if (allRequiredDependenciesAvailable() && (m_state == WAITING_FOR_REQUIRED)) {
178 activateService();
179 }
180 }
181
182 public synchronized void stop() {
183 if ((m_state != WAITING_FOR_REQUIRED) && (m_state != TRACKING_OPTIONAL)) {
184 if ((m_state > 0) && (m_state < STATE_NAMES.length)) {
185 throw new IllegalStateException("Cannot stop from state " + STATE_NAMES[m_state]);
186 }
187 throw new IllegalStateException("Cannot stop from unknown state.");
188 }
189 if (m_state == TRACKING_OPTIONAL) {
190 deactivateService();
191 }
192 stopTrackingRequired();
193 }
194
195 private void activateService() {
196 // service activation logic, first we initialize the service instance itself
197 // meaning it is created if necessary and the bundle context is set
198 initService();
199 // then we invoke the init callback so the service can further initialize
200 // itself
201 invoke(m_callbackInit);
202 // now is the time to configure the service, meaning all required
203 // dependencies will be set and any callbacks called
204 configureService();
205 // inform the state listeners we're starting
206 stateListenersStarting();
207 // start tracking optional services
208 startTrackingOptional();
209 // invoke the start callback, since we're now ready to be used
210 invoke(m_callbackStart);
211 // register the service in the framework's service registry
212 registerService();
213 // inform the state listeners we've started
214 stateListenersStarted();
215 }
216
217 private void deactivateService() {
218 // service deactivation logic, first inform the state listeners
219 // we're stopping
220 stateListenersStopping();
221 // then, unregister the service from the framework
222 unregisterService();
223 // invoke the stop callback
224 invoke(m_callbackStop);
225 // stop tracking optional services
226 stopTrackingOptional();
227 // inform the state listeners we've stopped
228 stateListenersStopped();
229 // invoke the destroy callback
230 invoke(m_callbackDestroy);
231 // destroy the service instance
232 destroyService();
233 }
234
235 private void invoke(String name) {
236 if (name != null) {
237 // invoke method if it exists
238 AccessibleObject.setAccessible(m_serviceInstance.getClass().getDeclaredMethods(), true);
239 try {
240 m_serviceInstance.getClass().getDeclaredMethod(name, null).invoke(m_serviceInstance, null);
241 }
242 catch (NoSuchMethodException e) {
243 // ignore this, we don't care if the method does not exist
244 }
245 catch (Exception e) {
246 // TODO handle this exception
247 e.printStackTrace();
248 }
249 }
250 }
251
252 private synchronized void stateListenersStarting() {
253 Iterator i = m_listeners.iterator();
254 while (i.hasNext()) {
255 ServiceStateListener ssl = (ServiceStateListener) i.next();
256 ssl.starting(this);
257 }
258 }
259
260 private synchronized void stateListenersStarted() {
261 Iterator i = m_listeners.iterator();
262 while (i.hasNext()) {
263 ServiceStateListener ssl = (ServiceStateListener) i.next();
264 ssl.started(this);
265 }
266 }
267
268 private synchronized void stateListenersStopping() {
269 Iterator i = m_listeners.iterator();
270 while (i.hasNext()) {
271 ServiceStateListener ssl = (ServiceStateListener) i.next();
272 ssl.stopping(this);
273 }
274 }
275
276 private synchronized void stateListenersStopped() {
277 Iterator i = m_listeners.iterator();
278 while (i.hasNext()) {
279 ServiceStateListener ssl = (ServiceStateListener) i.next();
280 ssl.stopped(this);
281 }
282 }
283
284 private boolean allRequiredDependenciesAvailable() {
285 Iterator i = getDependencies().iterator();
286 while (i.hasNext()) {
287 Dependency dependency = (Dependency) i.next();
288 if (dependency.isRequired() && !dependency.isAvailable()) {
289 return false;
290 }
291 }
292 return true;
293 }
294
295 private void startTrackingOptional() {
296 m_state = TRACKING_OPTIONAL;
297 Iterator i = getDependencies().iterator();
298 while (i.hasNext()) {
299 Dependency dependency = (Dependency) i.next();
300 if (!dependency.isRequired()) {
301 dependency.start(this);
302 }
303 }
304 }
305
306 private void stopTrackingOptional() {
307 m_state = WAITING_FOR_REQUIRED;
308 Iterator i = getDependencies().iterator();
309 while (i.hasNext()) {
310 Dependency dependency = (Dependency) i.next();
311 if (!dependency.isRequired()) {
312 dependency.stop(this);
313 }
314 }
315 }
316
317 private void startTrackingRequired() {
318 m_state = WAITING_FOR_REQUIRED;
319 Iterator i = getDependencies().iterator();
320 while (i.hasNext()) {
321 Dependency dependency = (Dependency) i.next();
322 if (dependency.isRequired()) {
323 dependency.start(this);
324 }
325 }
326 }
327
328 private void stopTrackingRequired() {
329 m_state = STOPPING;
330 Iterator i = getDependencies().iterator();
331 while (i.hasNext()) {
332 Dependency dependency = (Dependency) i.next();
333 if (dependency.isRequired()) {
334 dependency.stop(this);
335 }
336 }
337 }
338
339 private void initService() {
340 if (m_implementation instanceof Class) {
341 // instantiate
342 try {
343 m_serviceInstance = ((Class) m_implementation).newInstance();
344 } catch (InstantiationException e) {
345 // TODO handle this exception
346 e.printStackTrace();
347 } catch (IllegalAccessException e) {
348 // TODO handle this exception
349 e.printStackTrace();
350 }
351 }
352 else {
353 m_serviceInstance = m_implementation;
354 }
355 // configure the bundle context
356 configureImplementation(BundleContext.class, m_context);
357 configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
358
359 }
360
361 private void configureService() {
362 // configure all services (the optional dependencies might be configured
363 // as null objects but that's what we want at this point)
364 configureServices();
365 }
366
367 private void destroyService() {
368 unconfigureServices();
369 m_serviceInstance = null;
370 }
371
372 private void registerService() {
373 if (m_serviceName != null) {
374 ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
375 m_registration = wrapper;
376 configureImplementation(ServiceRegistration.class, wrapper);
377 // service name can either be a string or an array of strings
378 ServiceRegistration registration;
379 if (m_serviceName instanceof String) {
380 registration = m_context.registerService((String) m_serviceName, m_serviceInstance, m_serviceProperties);
381 }
382 else {
383 registration = m_context.registerService((String[]) m_serviceName, m_serviceInstance, m_serviceProperties);
384 }
385 wrapper.setServiceRegistration(registration);
386 }
387 }
388
389 private void unregisterService() {
390 if (m_serviceName != null) {
391 m_registration.unregister();
392 configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
393 }
394 }
395
396 private void updateInstance(Dependency dependency) {
397 if (dependency instanceof ServiceDependency) {
398 ServiceDependency sd = (ServiceDependency) dependency;
399 // update the dependency in the service instance (it will use
400 // a null object if necessary)
401 if (sd.isAutoConfig()) {
402 configureImplementation(sd.getInterface(), sd.getService());
403 }
404 }
405 }
406
407 /**
408 * Configure a field in the service implementation. The service implementation
409 * is searched for fields that have the same type as the class that was specified
410 * and for each of these fields, the specified instance is filled in.
411 *
412 * @param clazz the class to search for
413 * @param instance the instance to fill in
414 */
415 private void configureImplementation(Class clazz, Object instance) {
416 Class serviceClazz = m_serviceInstance.getClass();
417 while (serviceClazz != null) {
418 Field[] fields = serviceClazz.getDeclaredFields();
419 AccessibleObject.setAccessible(fields, true);
420 for (int j = 0; j < fields.length; j++) {
421 if (fields[j].getType().equals(clazz)) {
422 try {
423 // synchronized makes sure the field is actually written to immediately
424 synchronized (new Object()) {
425 fields[j].set(m_serviceInstance, instance);
426 }
427 }
428 catch (Exception e) {
429 System.err.println("Exception while trying to set " + fields[j].getName() +
430 " of type " + fields[j].getType().getName() +
431 " by classloader " + fields[j].getType().getClassLoader() +
432 " which should equal type " + clazz.getName() +
433 " by classloader " + clazz.getClassLoader() +
434 " of type " + serviceClazz.getName() +
435 " by classloader " + serviceClazz.getClassLoader() +
436 " on " + m_serviceInstance +
437 " by classloader " + m_serviceInstance.getClass().getClassLoader() +
438 "\nDumping stack:"
439 );
440 e.printStackTrace();
441 System.out.println("C: " + clazz);
442 System.out.println("I: " + instance);
443 System.out.println("I:C: " + instance.getClass().getClassLoader());
444 Class[] classes = instance.getClass().getInterfaces();
445 for (int i = 0; i < classes.length; i++) {
446 Class c = classes[i];
447 System.out.println("I:C:I: " + c);
448 System.out.println("I:C:I:C: " + c.getClassLoader());
449 }
450 System.out.println("F: " + fields[j]);
451 throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + m_serviceInstance);
452 }
453 }
454 }
455 serviceClazz = serviceClazz.getSuperclass();
456 }
457 }
458
459 private void configureServices() {
460 Iterator i = getDependencies().iterator();
461 while (i.hasNext()) {
462 Dependency dependency = (Dependency) i.next();
463 if (dependency instanceof ServiceDependency) {
464 ServiceDependency sd = (ServiceDependency) dependency;
465 if (sd.isAutoConfig()) {
466 configureImplementation(sd.getInterface(), sd.getService());
467 }
468 // for required dependencies, we invoke any callbacks here
469 if (sd.isRequired()) {
470 sd.invokeAdded();
471 }
472 }
473 }
474 }
475
476 private void unconfigureServices() {
477 Iterator i = getDependencies().iterator();
478 while (i.hasNext()) {
479 Dependency dependency = (Dependency) i.next();
480 if (dependency instanceof ServiceDependency) {
481 ServiceDependency sd = (ServiceDependency) dependency;
482 // for required dependencies, we invoke any callbacks here
483 if (sd.isRequired()) {
484 sd.invokeRemoved();
485 }
486 }
487 }
488 }
489
490 public synchronized void addStateListener(ServiceStateListener listener) {
491 m_listeners.add(listener);
492 if (m_state == TRACKING_OPTIONAL) {
493 listener.starting(this);
494 listener.started(this);
495 }
496 }
497
498 public synchronized void removeStateListener(ServiceStateListener listener) {
499 m_listeners.remove(listener);
500 }
501
502 synchronized void removeStateListeners() {
503 m_listeners.clear();
504 }
505
506 public synchronized Service setInterface(String serviceName, Dictionary properties) {
507 ensureNotActive();
508 m_serviceName = serviceName;
509 m_serviceProperties = properties;
510 return this;
511 }
512
513 public synchronized Service setInterface(String[] serviceName, Dictionary properties) {
514 ensureNotActive();
515 m_serviceName = serviceName;
516 m_serviceProperties = properties;
517 return this;
518 }
519
520 public synchronized Service setCallbacks(String init, String start, String stop, String destroy) {
521 ensureNotActive();
522 m_callbackInit = init;
523 m_callbackStart = start;
524 m_callbackStop = stop;
525 m_callbackDestroy = destroy;
526 return this;
527 }
528
529 public synchronized Service setImplementation(Object implementation) {
530 ensureNotActive();
531 m_implementation = implementation;
532 return this;
533 }
534
535 private void ensureNotActive() {
536 if ((m_state == TRACKING_OPTIONAL) || (m_state == WAITING_FOR_REQUIRED)) {
537 throw new IllegalStateException("Cannot modify state while active.");
538 }
539 }
540 boolean isRegistered() {
541 return (m_state == TRACKING_OPTIONAL);
542 }
543
544 public String toString() {
545 return "ServiceImpl[" + m_serviceName + " " + m_implementation + "]";
546 }
547
548 public synchronized Dictionary getServiceProperties() {
549 if (m_serviceProperties != null) {
550 return (Dictionary) ((Hashtable) m_serviceProperties).clone();
551 }
552 return null;
553 }
554
555 public synchronized void setServiceProperties(Dictionary serviceProperties) {
556 m_serviceProperties = serviceProperties;
557 if (isRegistered() && (m_serviceName != null) && (m_serviceProperties != null)) {
558 m_registration.setProperties(m_serviceProperties);
559 }
560 }
561
562 static {
563 NULL_REGISTRATION = (ServiceRegistration) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[] {ServiceRegistration.class}, new DefaultNullObject());
564 }
565}