4. Services
4.6. Introduction to Service Trackers
To address the excess verbosity of the code in Section4.4, we can refactor it to use a utility class provided by OSGi calledServiceTracker
In factServiceTrackeris one of the most important classes you will use as an OSGi programmer, and it has many more uses than just the one we will be taking advantage of in this code. But for now, the code in Listing4.10simply does the same thing as before, and in particular it is no better at dealing with a missingMailboxservice:
Listing 4.10Message Count Activator —ServiceTrackerversion 1 p a c k a g e o r g.o s g i.t u t o r i a l; 3 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; 4 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 E x c e p t i o n; 5 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; 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 C o n t e x t; 7 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; 9 p u b l i c c l a s s M e s s a g e C o u n t A c t i v a t o r 2 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 { 11 p r i v a t e S e r v i c e T r a c k e r m b o x T r a c k e r; 13 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 { 14 m b o x 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, M a i l b o x.c l a s s // 1 15 .g e t N a m e( ) , n u l l) ; 16 m b o x T r a c k e r.o p e n( ) ; // 2 17 p r i n t M e s s a g e C o u n t( ) ; 18 } 20 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 { 21 m b o x T r a c k e r.c l o s e( ) ; // 3 22 } 24 p r i v a t e v o i d p r i n t M e s s a g e C o u n t( ) t h r o w s M a i l b o x E x c e p t i o n { 25 M a i l b o x m b o x = (M a i l b o x) m b o x T r a c k e r.g e t S e r v i c e( ) ; // 4 26 i f (m b o x != n u l l) { 27 i n t c o u n t = m b o x.g e t A l l M e s s a g e s( ) .l e n g t h; // 5 28 S y s t e m.o u t.p r i n t l n(" T h e r e a r e " + c o u n t + " m e s s a g e s ") ; 29 } 30 } 31 }
4.6 Introduction to Service Trackers 89
At first glance this code is barely any shorter than the previous example! Nevertheless we have achieved something important: in exchange for a little extra initial effort, it is now much easier to call the service, as we can see in the methodprintMessageCount. In real code, we would probably make many separate calls to the service, so it is far better to repeat the four lines of code in thisprintMessageCountthan the nine lines of code in the previous version. The first difference, which we can see at marker1 of thestartmethod, is that instead of saving the bundle context directly into a field, we instead construct a new ServiceTracker field, passing it the bundle context and the name of the service that we are using it to track. Next at marker 2, we “open” the tracker, and at marker 3 in the stop() method we “close” it. We will look later at what is really going on under the covers when we open and close a service tracker, but for now just remember that the tracker will not work until it is opened.
The next difference is at marker4, where we callgetService on the tracker. Refreshingly, this immediately gives us the actual service object (if available) rather than aServiceReference. So we simply go ahead and call the service at marker 5, bearing in mind that we still need to check if the service was found. Also, we don’t need to clean up after ourselves with a finallyblock: we simply let the variable go out of scope, as the tracker will take care of releasing the service.
Here’s another interesting thing we can do with ServiceTracker. In the version of printMessageCount shown in Listing 4.11 we don’t want to fail immediately if the service is unavailable; instead we would like to wait up to five seconds for the service to become available.
Listing 4.11Waiting for a Service
1 p r i v a t e v o i d p r i n t M e s s a g e C o u n t(S t r i n g m e s s a g e) 2 t h r o w s I n t e r r u p t e d E x c e p t i o n, M a i l b o x E x c e p t i o n { 3 M a i l b o x m b o x = (M a i l b o x) m b o x T r a c k e r.w a i t F o r S e r v i c e( 5 0 0 0 ) ; 4 i f (m b o x != n u l l) { 5 i n t c o u n t = m b o x.g e t A l l M e s s a g e s( ) .l e n g t h; 6 S y s t e m.o u t.p r i n t l n(" T h e r e a r e " + c o u n t + " m e s s a g e s ") ; 7 } 8 }
When the mailbox service is available, waitForService works exactly the same asgetService: it will immediately return the service instance. However if the service is not currently available, the call will block until either the service becomes available or 5000 milliseconds has passed, whichever is sooner. Only after 5000 milliseconds has passed without the service becoming available will it returnnull.
However, waitForServiceneeds to be used with caution, and in particular it should not be called from thestartorstopmethods of aBundleActivator,
because those methods are supposed to return control quickly to the frame- work. This topic is discussed in more depth in Section??.