KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > framework > internal > core > ServiceRegistrationImpl


1 /*******************************************************************************
2  * Copyright (c) 2003, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.framework.internal.core;
13
14 import java.lang.reflect.Array JavaDoc;
15 import java.util.*;
16 import org.eclipse.osgi.framework.debug.Debug;
17 import org.eclipse.osgi.framework.util.Headers;
18 import org.osgi.framework.*;
19
20 /**
21  * A registered service.
22  *
23  * The framework returns a ServiceRegistration object when a
24  * {@link BundleContextImpl#registerService(String, Object, Dictionary) BundleContext.registerService}
25  * method is successful. This object is for the private use of
26  * the registering bundle and should not be shared with other bundles.
27  * <p>The ServiceRegistration object may be used to update the properties
28  * for the service or to unregister the service.
29  *
30  * <p>If the ServiceRegistration is garbage collected the framework may remove
31  * the service. This implies that if a
32  * bundle wants to keep its service registered, it should keep the
33  * ServiceRegistration object referenced.
34  */

35 //TODO That's kind of big
36
public class ServiceRegistrationImpl implements ServiceRegistration {
37     /** Reference to this registration. */
38     protected ServiceReferenceImpl reference;
39
40     /** Internal framework object. */
41     protected Framework framework;
42
43     /** context which registered this service. */
44     protected BundleContextImpl context;
45
46     /** bundle which registered this service. */
47     protected AbstractBundle bundle;
48
49     /** list of contexts using the service.
50      * Access to this should be protected by the registrationLock */

51     protected ArrayList contextsUsing;
52
53     /** service classes for this registration. */
54     protected String JavaDoc[] clazzes;
55
56     /** service object for this registration. */
57     protected Object JavaDoc service;
58
59     /** properties for this registration. */
60     protected Properties properties;
61
62     /** service id. */
63     protected long serviceid;
64
65     /** service ranking. */
66     protected int serviceranking;
67
68     /* internal object to use for synchronization */
69     protected Object JavaDoc registrationLock = new Object JavaDoc();
70
71     /** The registration state */
72     protected int state = REGISTERED;
73     public static final int REGISTERED = 0x00;
74     public static final int UNREGISTERING = 0x01;
75     public static final int UNREGISTERED = 0x02;
76
77     /**
78      * Construct a ServiceRegistration and register the service
79      * in the framework's service registry.
80      *
81      */

82     protected ServiceRegistrationImpl(BundleContextImpl context, String JavaDoc[] clazzes, Object JavaDoc service, Dictionary properties) {
83         this.context = context;
84         this.bundle = context.bundle;
85         this.framework = context.framework;
86         this.clazzes = clazzes; /* must be set before calling createProperties. */
87         this.service = service;
88         this.contextsUsing = null;
89         this.reference = new ServiceReferenceImpl(this);
90
91         synchronized (framework.serviceRegistry) {
92             serviceid = framework.getNextServiceId(); /* must be set before calling createProperties. */
93             this.properties = createProperties(properties); /* must be valid after unregister is called. */
94
95             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
96                 Debug.println("registerService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
97
}
98
99             framework.serviceRegistry.publishService(context, this);
100         }
101
102         /* must not hold the registrations lock when this event is published */
103         framework.publishServiceEvent(ServiceEvent.REGISTERED, reference);
104     }
105
106     /**
107      * Unregister the service.
108      * Remove a service registration from the framework's service
109      * registry.
110      * All {@link ServiceReferenceImpl} objects for this registration
111      * can no longer be used to interact with the service.
112      *
113      * <p>The following steps are followed to unregister a service:
114      * <ol>
115      * <li>The service is removed from the framework's service
116      * registry so that it may no longer be used.
117      * {@link ServiceReferenceImpl}s for the service may no longer be used
118      * to get a service object for the service.
119      * <li>A {@link ServiceEvent} of type {@link ServiceEvent#UNREGISTERING}
120      * is synchronously sent so that bundles using this service
121      * may release their use of the service.
122      * <li>For each bundle whose use count for this service is greater
123      * than zero:
124      * <ol>
125      * <li>The bundle's use count for this service is set to zero.
126      * <li>If the service was registered with a {@link ServiceFactory},
127      * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
128      * is called to release the service object for the bundle.
129      * </ol>
130      * </ol>
131      *
132      * @exception java.lang.IllegalStateException If
133      * this ServiceRegistration has already been unregistered.
134      * @see BundleContextImpl#ungetService
135      */

136     public void unregister() {
137         synchronized (registrationLock) {
138             if (state != REGISTERED) /* in the process of unregisterING */
139             {
140                 throw new IllegalStateException JavaDoc(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION);
141             }
142
143             /* remove this object from the service registry */
144             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
145                 Debug.println("unregisterService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
146
}
147
148             synchronized (framework.serviceRegistry) {
149                 framework.serviceRegistry.unpublishService(context, this);
150             }
151
152             state = UNREGISTERING; /* mark unregisterING */
153         }
154
155         /* must not hold the registrationLock when this event is published */
156         framework.publishServiceEvent(ServiceEvent.UNREGISTERING, reference);
157
158         /* we have published the ServiceEvent, now mark the service fully unregistered */
159         service = null;
160         state = UNREGISTERED;
161
162         int size = 0;
163         BundleContextImpl[] users = null;
164
165         synchronized (registrationLock) {
166             if (contextsUsing != null) {
167                 size = contextsUsing.size();
168
169                 if (size > 0) {
170                     if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
171                         Debug.println("unregisterService: releasing users"); //$NON-NLS-1$
172
}
173                     users = (BundleContextImpl[]) contextsUsing.toArray(new BundleContextImpl[size]);
174                 }
175             }
176         }
177
178         /* must not hold the registrationLock while releasing services */
179         for (int i = 0; i < size; i++) {
180             releaseService(users[i]);
181         }
182
183         contextsUsing = null;
184
185         reference = null; /* mark registration dead */
186         context = null;
187
188         /* These fields must be valid after unregister is called:
189          * properties
190          */

191     }
192
193     /**
194      * Returns a {@link ServiceReferenceImpl} object for this registration.
195      * The {@link ServiceReferenceImpl} object may be shared with other bundles.
196      *
197      * @exception java.lang.IllegalStateException If
198      * this ServiceRegistration has already been unregistered.
199      * @return A {@link ServiceReferenceImpl} object.
200      */

201     public org.osgi.framework.ServiceReference getReference() {
202         /* use reference instead of unregistered so that ServiceFactorys, called
203          * by releaseService after the registration is unregistered, can
204          * get the ServiceReference. Note this technically may voilate the spec
205          * but makes more sense.
206          */

207         if (reference == null) {
208             throw new IllegalStateException JavaDoc(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION);
209         }
210
211         return (reference);
212     }
213
214     /**
215      * Update the properties associated with this service.
216      *
217      * <p>The key "objectClass" cannot be modified by this method. It's
218      * value is set when the service is registered.
219      *
220      * <p>The following steps are followed to modify a service's properties:
221      * <ol>
222      * <li>The service's properties are replaced with the provided properties.
223      * <li>A {@link ServiceEvent} of type {@link ServiceEvent#MODIFIED}
224      * is synchronously sent.
225      * </ol>
226      *
227      * @param props The properties for this service.
228      * Changes should not be made to this object after calling this method.
229      * To update the service's properties this method should be called again.
230      * @exception java.lang.IllegalStateException If
231      * this ServiceRegistration has already been unregistered.
232      *
233      * @exception IllegalArgumentException If the <tt>properties</tt>
234      * parameter contains case variants of the same key name.
235      */

236     public void setProperties(Dictionary props) {
237         synchronized (registrationLock) {
238             if (state != REGISTERED) /* in the process of unregistering */
239             {
240                 throw new IllegalStateException JavaDoc(Msg.SERVICE_ALREADY_UNREGISTERED_EXCEPTION);
241             }
242
243             this.properties = createProperties(props);
244         }
245
246         /* must not hold the registrationLock when this event is published */
247         framework.publishServiceEvent(ServiceEvent.MODIFIED, reference);
248     }
249
250     /**
251      * Construct a properties object from the dictionary for this
252      * ServiceRegistration.
253      *
254      * @param props The properties for this service.
255      * @return A Properties object for this ServiceRegistration.
256      */

257     protected Properties createProperties(Dictionary props) {
258         Properties properties = new Properties(props);
259
260         properties.set(Constants.OBJECTCLASS, clazzes, true);
261         properties.set(Constants.SERVICE_ID, new Long JavaDoc(serviceid), true);
262         properties.setReadOnly();
263         Object JavaDoc ranking = properties.getProperty(Constants.SERVICE_RANKING);
264
265         serviceranking = (ranking instanceof Integer JavaDoc) ? ((Integer JavaDoc) ranking).intValue() : 0;
266
267         return (properties);
268     }
269
270     /**
271      * Get the value of a service's property.
272      *
273      * <p>This method will continue to return property values after the
274      * service has been unregistered. This is so that references to
275      * unregistered service can be interrogated.
276      * (For example: ServiceReference objects stored in the log.)
277      *
278      * @param key Name of the property.
279      * @return Value of the property or <code>null</code> if there is
280      * no property by that name.
281      */

282     protected Object JavaDoc getProperty(String JavaDoc key) {
283         synchronized (registrationLock) {
284             return (properties.getProperty(key));
285         }
286     }
287
288     /**
289      * Get the list of key names for the service's properties.
290      *
291      * <p>This method will continue to return the keys after the
292      * service has been unregistered. This is so that references to
293      * unregistered service can be interrogated.
294      * (For example: ServiceReference objects stored in the log.)
295      *
296      * @return The list of property key names.
297      */

298     protected String JavaDoc[] getPropertyKeys() {
299         synchronized (registrationLock) {
300             return (properties.getPropertyKeys());
301         }
302     }
303
304     /**
305      * Return the bundle which registered the service.
306      *
307      * <p>This method will always return <code>null</code> when the
308      * service has been unregistered. This can be used to
309      * determine if the service has been unregistered.
310      *
311      * @return The bundle which registered the service.
312      */

313     protected AbstractBundle getBundle() {
314         if (reference == null) {
315             return (null);
316         }
317
318         return (bundle);
319     }
320
321     /**
322      * Get a service object for the using BundleContext.
323      *
324      * @param user BundleContext using service.
325      * @return Service object
326      */

327     protected Object JavaDoc getService(BundleContextImpl user) {
328         synchronized (registrationLock) {
329             if (state == UNREGISTERED) /* service unregistered */
330             {
331                 return (null);
332             }
333
334             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
335                 Debug.println("getService[" + user.bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
336
}
337
338             Hashtable servicesInUse = user.servicesInUse;
339
340             ServiceUse use = (ServiceUse) servicesInUse.get(reference);
341
342             if (use == null) {
343                 use = new ServiceUse(user, this);
344
345                 Object JavaDoc service = use.getService();
346
347                 if (service != null) {
348                     servicesInUse.put(reference, use);
349
350                     if (contextsUsing == null) {
351                         contextsUsing = new ArrayList(10);
352                     }
353
354                     contextsUsing.add(user);
355                 }
356
357                 return (service);
358             } else {
359                 return (use.getService());
360             }
361         }
362     }
363
364     /**
365      * Unget a service for the using BundleContext.
366      *
367      * @param user BundleContext using service.
368      * @return <code>false</code> if the context bundle's use count for the service
369      * is zero or if the service has been unregistered,
370      * otherwise <code>true</code>.
371      */

372     protected boolean ungetService(BundleContextImpl user) {
373         synchronized (registrationLock) {
374             if (state == UNREGISTERED) {
375                 return (false);
376             }
377
378             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
379                 String JavaDoc bundle = (user.bundle == null) ? "" : user.bundle.toString(); //$NON-NLS-1$
380
Debug.println("ungetService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
381
}
382
383             Hashtable servicesInUse = user.servicesInUse;
384
385             if (servicesInUse != null) {
386                 ServiceUse use = (ServiceUse) servicesInUse.get(reference);
387
388                 if (use != null) {
389                     if (use.ungetService()) {
390                         /* use count is now zero */
391                         servicesInUse.remove(reference);
392
393                         contextsUsing.remove(user);
394                     }
395                     return (true);
396                 }
397             }
398
399             return (false);
400         }
401     }
402
403     /**
404      * Release the service for the using BundleContext.
405      *
406      * @param user BundleContext using service.
407      */

408     protected void releaseService(BundleContextImpl user) {
409         synchronized (registrationLock) {
410             if (reference == null) /* registration dead */
411             {
412                 return;
413             }
414
415             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
416                 String JavaDoc bundle = (user.bundle == null) ? "" : user.bundle.toString(); //$NON-NLS-1$
417
Debug.println("releaseService[" + bundle + "](" + this + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
418
}
419
420             Hashtable servicesInUse = user.servicesInUse;
421
422             if (servicesInUse != null) {
423                 ServiceUse use = (ServiceUse) servicesInUse.remove(reference);
424
425                 if (use != null) {
426                     use.releaseService();
427                     // contextsUsing may have been nulled out by use.releaseService() if the registrant bundle
428
// is listening for events and unregisters the service
429
if (contextsUsing != null)
430                         contextsUsing.remove(user);
431                 }
432             }
433         }
434     }
435
436     /**
437      * Return the list of bundle which are using this service.
438      *
439      * @return Array of Bundles using this service.
440      */

441     protected AbstractBundle[] getUsingBundles() {
442         synchronized (registrationLock) {
443             if (state == UNREGISTERED) /* service unregistered */
444                 return (null);
445
446             if (contextsUsing == null)
447                 return (null);
448
449             int size = contextsUsing.size();
450             if (size == 0)
451                 return (null);
452
453             /* Copy list of BundleContext into an array of Bundle. */
454             AbstractBundle[] bundles = new AbstractBundle[size];
455             for (int i = 0; i < size; i++)
456                 bundles[i] = ((BundleContextImpl) contextsUsing.get(i)).bundle;
457
458             return (bundles);
459         }
460     }
461
462     /**
463      * Return a String representation of this object.
464      *
465      * @return String representation of this object.
466      */

467     public String JavaDoc toString() {
468         String JavaDoc[] clazzes = this.clazzes;
469         int size = clazzes.length;
470         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(50 * size);
471
472         sb.append('{');
473
474         for (int i = 0; i < size; i++) {
475             if (i > 0) {
476                 sb.append(", "); //$NON-NLS-1$
477
}
478             sb.append(clazzes[i]);
479         }
480
481         sb.append("}="); //$NON-NLS-1$
482
sb.append(properties);
483
484         return (sb.toString());
485     }
486
487     /**
488      * Hashtable for service properties.
489      */

490     static class Properties extends Headers {
491         /**
492          * Create a properties object for the service.
493          *
494          * @param props The properties for this service.
495          */

496         private Properties(int size, Dictionary props) {
497             super(size);
498
499             if (props != null) {
500                 synchronized (props) {
501                     Enumeration keysEnum = props.keys();
502
503                     while (keysEnum.hasMoreElements()) {
504                         Object JavaDoc key = keysEnum.nextElement();
505
506                         if (key instanceof String JavaDoc) {
507                             String JavaDoc header = (String JavaDoc) key;
508
509                             setProperty(header, props.get(header));
510                         }
511                     }
512                 }
513             }
514         }
515
516         /**
517          * Create a properties object for the service.
518          *
519          * @param props The properties for this service.
520          */

521         protected Properties(Dictionary props) {
522             this((props == null) ? 2 : props.size() + 2, props);
523         }
524
525         /**
526          * Get a clone of the value of a service's property.
527          *
528          * @param key header name.
529          * @return Clone of the value of the property or <code>null</code> if there is
530          * no property by that name.
531          */

532         protected Object JavaDoc getProperty(String JavaDoc key) {
533             return (cloneValue(get(key)));
534         }
535
536         /**
537          * Get the list of key names for the service's properties.
538          *
539          * @return The list of property key names.
540          */

541         protected synchronized String JavaDoc[] getPropertyKeys() {
542             int size = size();
543
544             String JavaDoc[] keynames = new String JavaDoc[size];
545
546             Enumeration keysEnum = keys();
547
548             for (int i = 0; i < size; i++) {
549                 keynames[i] = (String JavaDoc) keysEnum.nextElement();
550             }
551
552             return (keynames);
553         }
554
555         /**
556          * Put a clone of the property value into this property object.
557          *
558          * @param key Name of property.
559          * @param value Value of property.
560          * @return previous property value.
561          */

562         protected synchronized Object JavaDoc setProperty(String JavaDoc key, Object JavaDoc value) {
563             return (set(key, cloneValue(value)));
564         }
565
566         /**
567          * Attempt to clone the value if necessary and possible.
568          *
569          * For some strange reason, you can test to see of an Object is
570          * Cloneable but you can't call the clone method since it is
571          * protected on Object!
572          *
573          * @param value object to be cloned.
574          * @return cloned object or original object if we didn't clone it.
575          */

576         protected static Object JavaDoc cloneValue(Object JavaDoc value) {
577             if (value == null)
578                 return null;
579             if (value instanceof String JavaDoc) /* shortcut String */
580                 return (value);
581             if (value instanceof Number JavaDoc) /* shortcut Number */
582                 return value;
583             if (value instanceof Character JavaDoc) /* shortcut Character */
584                 return value;
585             if (value instanceof Boolean JavaDoc) /* shortcut Boolean */
586                 return value;
587
588             Class JavaDoc clazz = value.getClass();
589             if (clazz.isArray()) {
590                 // Do an array copy
591
Class JavaDoc type = clazz.getComponentType();
592                 int len = Array.getLength(value);
593                 Object JavaDoc clonedArray = Array.newInstance(type, len);
594                 System.arraycopy(value, 0, clonedArray, 0, len);
595                 return clonedArray;
596             }
597             // must use reflection because Object clone method is protected!!
598
try {
599                 return (clazz.getMethod("clone", null).invoke(value, null)); //$NON-NLS-1$
600
} catch (Exception JavaDoc e) {
601                 /* clone is not a public method on value's class */
602             } catch (Error JavaDoc e) {
603                 /* JCL does not support reflection; try some well known types */
604                 if (value instanceof Vector)
605                     return (((Vector) value).clone());
606                 if (value instanceof Hashtable)
607                     return (((Hashtable) value).clone());
608             }
609             return (value);
610         }
611
612         public synchronized String JavaDoc toString() {
613             String JavaDoc keys[] = getPropertyKeys();
614
615             int size = keys.length;
616
617             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(20 * size);
618
619             sb.append('{');
620
621             int n = 0;
622             for (int i = 0; i < size; i++) {
623                 String JavaDoc key = keys[i];
624                 if (!key.equals(Constants.OBJECTCLASS)) {
625                     if (n > 0)
626                         sb.append(", "); //$NON-NLS-1$
627

628                     sb.append(key);
629                     sb.append('=');
630                     Object JavaDoc value = get(key);
631                     if (value.getClass().isArray()) {
632                         sb.append('[');
633                         int length = Array.getLength(value);
634                         for (int j = 0; j < length; j++) {
635                             if (j > 0)
636                                 sb.append(',');
637                             sb.append(Array.get(value, j));
638                         }
639                         sb.append(']');
640                     } else {
641                         sb.append(value);
642                     }
643                     n++;
644                 }
645             }
646
647             sb.append('}');
648
649             return (sb.toString());
650         }
651     }
652 }
653
Popular Tags