4. Services
4.5. Service Properties
In addition to the interface name, we may wish to associate additional meta- data with our services. For example, in this chapter we have registered several instances of theMailboxservice, each with different characteristics. It would be nice to tell clients something about each mailbox so that they can, if they wish, obtain only one specific mailbox, or at least report something to the user about what kind of mailbox they have obtained. This is done with service properties.
Recall that when registering the service in Section4.2, we left the final param- eternull. Instead we can pass in ajava.util.Propertiesobject containing the properties that we want to set on the service5.
5Actually the type of this parameter is
java.util.Dictionary, of which
java.util.Properties is a sub-class. Why not use the java.util.Map interface,
which the Propertiesclass also implements? Simply becauseMap has “only” existed since Java 1.2, so it cannot be used on all platforms supported by OSGi!
4.5 Service Properties 87
Let’s modify WelcomeMailboxActivator to add a “mailbox name” property to the service. The newstart method is shown in Listing4.9.
Listing 4.9 Adding Service Properties to the Welcome Mailbox 1 p u b l i c v o i d s t a r t(B u n d l e C o n t e x t c o n t e x t) t h r o w s E x c e p t i o n { 2 M a i l b o x m b o x = n e w F i x e d M a i l b o x( ) ; 4 P r o p e r t i e s p r o p s = n e w P r o p e r t i e s( ) ; 5 p r o p s.p u t(M a i l b o x.N A M E _ P R O P E R T Y, " w e l c o m e ") ; 6 c o n t e x t.r e g i s t e r S e r v i c e(M a i l b o x.c l a s s.g e t N a m e( ) , m b o x, p r o p s) ; 7 }
Note that we avoid hard-coding the property name everywhere we use it, as this can be error prone and difficult to change later. Instead we use a constant that was defined in the Mailbox interface itself. This is a suitable place to put the constant because it is guaranteed to be visible to both service implementers and service consumers.
Try rebuilding this bundle and updating it in Equinox. If we now re-run the servicescommand we should see the property has been added to the service: o s g i> s e r v i c e s . . . {o r g.o s g i.b o o k.r e a d e r.a p i.M a i l b o x}={m a i l b o x N a m e=w e l c o m e,s e r v i c e.i d=27} R e g i s t e r e d b y b u n d l e: w e l c o m e _ m a i l b o x _ 0. 0 . 0 [ 2 ] N o b u n d l e s u s i n g s e r v i c e. . . .
The other entry we see here, service.id, is a built-in property that has been added by the framework. Another built-in property, objectClass, is also added: this indicates the interface name that the service is published under. It is a property like mailboxName and service.id, but because it is such an important property Equinox chooses to bring its value to the front of the entry. There are other standard property names defined in the OSGi specification, but only service.idandobjectClass are mandatory, so they appear on every service. These two, and the rest, can be found in the class org.osgi.framework.Constants.
To query the properties on a service, we simply call thegetPropertymethod of the ServiceReferenceto get a single property value. If we need to know about all of the properties on the service we can callgetPropertyKeysmethod to list them.
Service properties provide a clue as to why we need a two-stage process for looking up a service, i.e. first obtaining aServiceReferencebefore obtaining the actual service. One reason for this is sometimes we don’t need the service object (or at least notyet) but only its properties. When we obtain the service object, the OSGi framework must keep track of the fact we are using it, creating a very small amount of overhead. It is sensible to avoid that overhead when it is not needed.
Also, service references are small, lightweight objects that can be passed around, copied or discarded at will. The framework does not track the service reference objects it hands out. Also service reference objects can be passed easily between bundles, whereas it can cause problems to pass an actual service instance to another bundle: that would prevent the framework from being able to track which bundles are using each service. In situations where we want to ask another bundle to do something with a service, we should pass the reference and let the other bundle callgetService andungetServiceitself.