• No results found

4. Services

4.8. Tracking Services

Although we saw ServiceTracker in Section 4.6, we only used it in a very limited way. In this section we will see the main purpose forServiceTracker: hiding the complexities of listening to and consuming dynamic services. Rather than simply “listening”, which is passive, we wish to actively “track” the ser- vices we depend on.

Before proceeding to the example code, we will need to have an implementation of the DbMailboxclass but, like FileMailbox, the actual implementation is not interesting as part of this exposition. Therefore we will use another stub class — the full definition of which is left as an exercise.

Let’s write a bundle activator usingServiceTracker: this is shown in Listing

4.12. The start andstop methods of this activator look very similar to our first example of using a tracker in Section4.6: we simply create and open the tracker on start-up (markers1 and 2), and close it on shutdown (marker3). However this time the third parameter to the constructor ofServiceTrackeris notnull. Instead, we pass in an instance of theServiceTrackerCustomizer interface, which tells the tracker what to do when services are added, removed or modified.

Our customizer simply registers aDbMailboxservice whenever aDataSource service is added, and unregisters it when theDataSource service is removed. Why does this not suffer from the same limitations as theServiceListener- based approach described Section4.7? Simply because, unlike a listener, the adding and removed methods of ServiceTracker are called not only when the state of a service changes but also when the tracker is opened, to notify us of pre-existing services. TheaddingService() method is called multiple times when the tracker is opened, once for each service currently registered, and it is also called whenever a new service is registered at any time later for as long as the tracker is open. Furthermore theremovedService() is called any time a service that we have been previously been notified of goes away, and it is also called for each service when the tracker closes. Therefore we can deal with services in a uniform fashion without needing to distinguish between pre-existing services and ones that are registered while our listener is active. This greatly simplifies the code we need to write.

Incidentally theServiceTrackerclass does not use any special hooks into the framework, it builds on the existing facilities that we have already seen. When we callopen()on a service tracker, it hooks up aServiceListenerand then

4.8 Tracking Services 93

Listing 4.12Database Mailbox Activator 1 p a c k a g e o r g.o s g i.b o o k.r e a d e r.d b m a i l b o x; 3 i m p o r t j a v a x.s q l.D a t a S o u r c e; 5 i m p o r t 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; 6 i m p o r t o r g.o s g i.f r a m e w o r k.B u n d l e A c t i v a t o r; 7 i m p o r t o r g.o s g i.f r a m e w o r k.B u n d l e C o n t e x t; 8 i m p o r t o r g.o s g i.f r a m e w o r k.S e r v i c e R e f e r e n c e; 9 i m p o r t o r g.o s g i.f r a m e w o r k.S e r v i c e R e g i s t r a t i o n; 10 i m p o r t o r g.o s g i.u t i l.t r a c k e r.S e r v i c e T r a c k e r; 11 i m p o r t o r g.o s g i.u t i l.t r a c k e r.S e r v i c e T r a c k e r C u s t o m i z e r; 13 p u b l i c c l a s s D b M a i l b o x A c t i v a t o r i m p l e m e n t s B u n d l e A c t i v a t o r { 15 p r i v a t e B u n d l e C o n t e x t c o n t e x t; 16 p r i v a t e S e r v i c e T r a c k e r t r a c k e r; 18 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 { 19 t h i s.c o n t e x t = c o n t e x t; 21 t r a c k e r = n e w S e r v i c e T r a c k e r(c o n t e x t, D a t a S o u r c e.c l a s s 22 .g e t N a m e( ) , n e w D S C u s t o m i z e r( ) ) ; // 1 23 t r a c k e r.o p e n( ) ; // 2 24 } 26 p u b l i c v o i d s t o p(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 { 27 t r a c k e r.c l o s e( ) ; // 3 28 } 30 p r i v a t e c l a s s D S C u s t o m i z e r i m p l e m e n t s S e r v i c e T r a c k e r C u s t o m i z e r { 32 p u b l i c O b j e c t a d d i n g S e r v i c e(S e r v i c e R e f e r e n c e r e f) { 33 D a t a S o u r c e d s = (D a t a S o u r c e) c o n t e x t.g e t S e r v i c e(r e f) ; // 4 35 D b M a i l b o x m b o x = n e w D b M a i l b o x(d s) ; 36 S e r v i c e R e g i s t r a t i o n r e g i s t r a t i o n = c o n t e x t.r e g i s t e r S e r v i c e( 37 M a i l b o x.c l a s s.g e t N a m e( ) , m b o x, n u l l) ; // 5 39 r e t u r n r e g i s t r a t i o n; // 6 40 } 42 p u b l i c v o i d m o d i f i e d S e r v i c e(S e r v i c e R e f e r e n c e r e f, 43 O b j e c t s e r v i c e) { 44 } 46 p u b l i c v o i d r e m o v e d S e r v i c e(S e r v i c e R e f e r e n c e r e f, O b j e c t s e r v i c e) { 47 S e r v i c e R e g i s t r a t i o n r e g i s t r a t i o n = 48 (S e r v i c e R e g i s t r a t i o n) s e r v i c e; // 7 50 r e g i s t r a t i o n.u n r e g i s t e r( ) ; // 8 51 c o n t e x t.u n g e t S e r v i c e(r e f) ; // 9 52 } 53 } 54 }

scans the pre-existing services, eliminates duplicates etc. The internal code is still complex, but it has been written for us (and exhaustively tested) to save us from having to do it ourselves.

Let’s look at the customizer class in a little more detail. At marker4we receive a ServiceReference from the tracker that points at the newly registered service, and we ask the framework to de-reference it to produce the actual service object. We use the result to create a new instance ofDbMailbox and at marker5 we register it as a new mailbox service. At marker 6 we return the registration object back to the tracker.

Why return the registration object? The signature ofaddingService()simply has a return type ofObject; the tracker does not care what type of object we return, and we can return anything we like. However the tracker promises to remember the object (unless we returnnull, see below) and give it back to us in the following situations:

• When we call getService, the tracker will return whatever object we returned fromaddingService.

• When the underlying service is modified or unregistered, the tracker will callmodifiedServiceorremovedServicerespectively, passing both the service reference and the object that we returned from addingService. Because of the first one of these promises, it’s conventional to return the actual service object fromaddingService — which in the code above would be the ds variable. But that is not a requirement7. In general we should return whatever will be most useful for us to find the information or data structure that might need to be updated later. In the above code we want to “update” the registration of the mailbox service, so we return the registration object. Other times we might be storing information into aMap, so we return one of the keys from theMap.

An exception to this rule is when we returnnullfromaddingService, which the tracker takes to mean that we don’t care about this particular service refer- ence. That is, if we returnnullfromaddingService, the tracker will “forget” that particular service reference and will not call eithermodifiedServiceor removedServicelater if it is modified or removed. Thus returningnull can be used as a kind of filter, but in the next section we will see a more convenient way to apply filters declaratively.

Looking back at our example, since we know that the second parameter of removedServicewill be of typeServiceRegistration, we are able to cast it back to that type at marker7. Then we can simply unregister it (marker8) and “unget” theDataSource service (marker 9). The final step is necessary as the mirror image of callinggetService in the adding method.

7The API documentation forServiceTrackerCustomizerstates that we “should” return the