4. Services
4.4. Looking up a Service
Having seen how to register and unregister services, the next logical step is to look at how to look up and call methods on those services.
Perhaps surprising, this can be a little tricky. The problem is that services, as we have seen, can come and go at any time. If we look for a service at a particular instant, we might not find it, but we would find it if we looked two seconds later. Alternatively we could access a service twice in a row, but get a different service the second time because the one we got first time is no longer around.
Fortunately there is lots of support, both in the OSGi specifications themselves and in external third-party libraries, to help us to abstract away this complex- ity. In fact programming with dynamic services in OSGi need be hardly any more complex than with static dependency injection, yet it is far more power- ful. However, in this section we will look at the most low-level way of accessing services. This is partially to provide a firm base of understanding for when we get onto the more convenient approaches, and partially to drive home the truly dynamic nature of OSGi services.
Suppose we wish to write a bundle that accesses one of theMailboxservices and prints the current total number of messages in that mailbox. To keep things as simple as possible we will do this in the start() method of an activator, for exampleMessageCountActivatorin Listing4.7.
At marker1, we ask the framework to find aServiceReferencefor a named Java interface. As before, we avoid encoding the interface name as a literal string.
After checking that the service reference is not null, meaning the mailbox service is currently available, we proceed to request the actual service object at marker 2. Again we have to check if the framework returnednull — this is because although the service was available at marker1, it may have become unavailable in the time it took us to reach marker2.
At marker3 we actually call the service. Because ourmboxvariable holds the actual service object rather than any kind of proxy, we can call the methods on it just like any normal Java object.
Finally at marker 4 we “un-get” the service — in other words we let the framework know that we are no longer using it. This is necessary because the framework maintains a count of how many bundles are using a particular
4.4 Looking up a Service 85
Listing 4.7 Message Count Activator 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.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 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 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 B u n d l e C o n t e x t c o n t e x t; 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 t h i s.c o n t e x t = c o n t e x t; 15 p r i n t M e s s a g e C o u n t( ) ; 16 } 18 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 { 19 } 21 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 { 22 S e r v i c e R e f e r e n c e r e f = c o n t e x t // 1 23 .g e t S e r v i c e R e f e r e n c e(M a i l b o x.c l a s s.g e t N a m e( ) ) ; 25 i f (r e f != n u l l) { 26 M a i l b o x m b o x = (M a i l b o x) c o n t e x t.g e t S e r v i c e(r e f) ; // 2 27 i f (m b o x != n u l l) { 28 t r y { 29 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; // 3 30 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 ") ; 31 } f i n a l l y { 32 c o n t e x t.u n g e t S e r v i c e(r e f) ; // 4 33 } 34 } 35 } 36 } 37 }
service: when that count is zero, it knows that the service can safely be re- moved when the bundle providing it is deactivated. We have placed the call to ungetService()inside a finallyblock to ensure that it is always called on exit from our method, even if an uncaught exception occurs.
Listing4.8shows the Bnd descriptor.
Listing 4.8Bnd Descriptor for the Message Counter Bundle # m e s s a g e _ c o u n t.b n d
P r i v a t e−P a c k a g e: o r g.o s g i.t u t o r i a l
B u n d l e−A c t i v a t o r: o r g.o s g i.t u t o r i a l.M e s s a g e C o u n t A c t i v a t o r
Stepping back from the code, you may wonder why accessing a service requires a two-stage process of first obtaining a reference and then obtaining the actual service object. We will look at the reasons for this in the next section. The main problem with this code is that it’s just too long! Simply to make a single call to a service, we have to write two separatenullchecks and atry/ finallyblock, along with several noisy method calls to the OSGi framework. We certainly don’t want to repeat all of this each time we access a service. Also, the code does not behave particularly well when theMailbox service is unavailable: it simply gives up and prints nothing. We could at least print an error message, but even that is unsatifactory: what if we reallyneed to know how many messages are in the mailbox? The information will be available as soon as the mailbox service is registered, but it’s just bad luck that the above bundle activator has been called first.