Sorry to reiterate something from earlier, but:

> I'm trying to get a clear picture of the ServiceTracker. I've looked at some 
> examples (e.g. http://www.aqute.biz/Snippets/Tracker 
> <http://www.aqute.biz/Snippets/Tracker>), but they all seem rather 
> complicated.


Have you considered using declarative services?

The whole raison d’être for DS is to handle these scenarios simply and without 
boilerplate. It completely avoids these headaches. Using a ServiceTracker 
instead is lower level and is intrinsically more complicated.


As BJ suggested:

> @Component(immediate=true)
> public class MyComponentImpl {
>   @Reference
>   private LogService myLogService;
>   @Activate
>   private void startThreads() {
>      // Start the thread(s) that refer to (use) myLogService
>   }
>   @Deactivate
>   private void stopThreads() {
>      // Interrupt and join the thread(s) that refer to (use) myLogService
>   }
> }


will do what you said that you wanted. It is simple and concise, and you can 
rely on the LogService being registered at all points from before the beginning 
of the call to startThreads() to after the end of stopThreads(). Note that as 
Peter said, this does not mean that the LogService will not throw Exceptions at 
you, but the field will be non-null, and the reference “safe” to use.

Assembling the equivalent function with a ServiceTracker is harder, and you can 
get into trouble depending as to how your threads access the log service. You 
gave the example code:

> volatile LogService logService
> 
>     @Override
>     public LogService addingService(ServiceReference<LogService> reference) {
>          myLogService = super.addingService(reference);
>          // Start the thread(s) that refer to (use) myLogService
>          return myLogService;
>     }
>     
>     @Override
>     public void removedService(ServiceReference<LogService> reference,
>                                LogService service) {
>         // Interrupt and join the thread(s) that refer to (use) myLogService
>         myLogService = null;
>         super.removedService(reference, service);
>     }

There some obvious problems with the above code, and some more subtle problems 
too:

There could be more than one LogService registered (lets call them A and B). At 
this point adding is called twice, once for A then once for B. If later A is 
unregistered then we null out the reference to B, even though it is still 
registered.
If multiple services are added then multiple sets of threads will be started.
If any service is unregistered then all threads are stopped.
If the threads use the logService field then it may change over the course of 
the thread’s life (i.e. it may start using A then switch to B part way)
If the threads use ServiceTracker.getService() to access the LogService then 
they *may* get null back. There is a race condition between the threads being 
interrupted, and the point at which the tracked object is removed from the 
ServiceTracker. In particular the tracked object is removed from the tracker 
before the call to removedService 

In summary, I would strongly recommend using Declarative Services for this 
problem. It will save you a lot of time and pain.

Regards,

Tim

> On 15 Apr 2016, at 10:56, Michael Lipp <m...@mnl.de> wrote:
> 
> 
>> I think you confuse the validity of a service with its life cycle. There is 
>> -never- a guarantee that a service is valid. Just think of a service that is 
>> backed by a remote connection, if the cable is cut there is no way you can 
>> call that service anymore even though it is still registered. There are an 
>> infinite amount of cases where a service can fail before it is unregistered. 
> I don't know if these terms have special OSGi semantics, but "valid" means to 
> me that the service is there, i.e. the service object exists and -- more 
> important -- can be used, i.e. it's legal (with respect to the service 
> object's life cycle) to invoke its methods. (Of course, method invocations 
> can fail as e.g. Input/OutputStreams indicate by the IOExceptions they throw 
> -- special case RemoteException -- or PreparedStatement.executeUpdate 
> indicates by an SQLException; these cases have obviously to be handled 
> somehow in my code when I invoke a service's method).
> 
> What irritates me with a lot of OSGi code examples is the check for "null" 
> when getting the service. Of course, if I cannot be sure that it "is there" 
> and "can be used" (i.e. if I cannot be sure that it is "valid") then I have 
> to check (that's the case in the usual OSGi examples, because they start the 
> "example's service" without considering the availability of the service(s) 
> they rely on -- just as in your snippet http://www.aqute.biz/Snippets/Tracker 
> <http://www.aqute.biz/Snippets/Tracker>, that I referred to, where you simply 
> fall back to System.err if the log service is not available).
> 
> In general, if my service relies on another service and I want to invoke one 
> of that service's methods, I don't want to write code that first checks for 
> and then handles the special case that the service isn't valid (isn't there 
> or isn't usable with respect to its life cycle). I simply want my code to run 
> only if the service I rely on is available in the first place. (Which implies 
> that I *don't* have to check for null when getting the service.) So, if my 
> own code (the threads providing the service) runs only between invocations of 
> "addingService" and "removedService" (tracking the service I rely on, as in 
> my initial sample code), then (from my understanding) it     should be 
> unnecessary to check whether ServiceTracker.getService() returns null. 
> Between those calls it *must* return a service object, right? (This is what I 
> wanted to clarify.)
> 
>  - Michael
> 
>> 
>> The life cycle of services is designed so you can properly clean up your own 
>> data structures when a service disappears. However, there is NEVER a 
>> guarantee that you can still call the service. 
>> 
>> Kind regards,
>> 
>>      Peter Kriens
>> 
>> 
>> 
>>> On 15 apr. 2016, at 10:45, Michael Lipp <m...@mnl.de <mailto:m...@mnl.de>> 
>>> wrote:
>>> 
>>> BJ Hargrave:
>>>> The tense of the customizer method name tells you when it is called 
>>>> relative to when the tracking event occurred. addingService is "ing" so it 
>>>> is in the process of being tracked and is thus before the service becomes 
>>>> tracked. removedService is "ed" so it is past tense and thus after the 
>>>> service is no longer tracked but it is called during the service's 
>>>> unregistration so the service being untracked is still valid at the time 
>>>> of the removedService call.
>>> The point is that the validity of the service "while being added" or "after 
>>> having been removed" cannot be derived from the tense. Being notified 
>>> "after the service has been removed" actually suggests to me that it is no 
>>> longer there and therefore invalid. So I think it would be nice to have a 
>>> clear statement about the validity (just like yours above) in the 
>>> documentation of "addingService" and "removedService". Maybe it's already 
>>> stated somewhere else, but I failed to find it. Maybe I should be able to 
>>> derive it as obvious from the context, but I failed with that as well. (The 
>>> documentation of UNREGISTERING is much clearer in that respect.)
>>> 
>>> Thanks for the quick response and clarification.
>>> 
>>>  - Michael
>>> 
>>>>  
>>>> --
>>>> 
>>>> BJ Hargrave
>>>> Senior Technical Staff Member, IBM // office: +1 386 848 1781
>>>> OSGi Fellow and CTO of the OSGi Alliance // mobile: +1 386 848 3788
>>>> hargr...@us.ibm.com <mailto:hargr...@us.ibm.com>
>>>>  
>>>>  
>>>> ----- Original message -----
>>>> From: Michael Lipp  <mailto:m...@mnl.de><m...@mnl.de> <mailto:m...@mnl.de>
>>>> Sent by:  
>>>> <mailto:osgi-dev-boun...@mail.osgi.org>osgi-dev-boun...@mail.osgi.org 
>>>> <mailto:osgi-dev-boun...@mail.osgi.org>
>>>> To:  <mailto:osgi-dev@mail.osgi.org>osgi-dev@mail.osgi.org 
>>>> <mailto:osgi-dev@mail.osgi.org>
>>>> Cc:
>>>> Subject: [osgi-dev] Clarify usage of ServiceTracker
>>>> Date: Wed, Apr 13, 2016 4:47 PM
>>>>  
>>>> Hi,
>>>> 
>>>> I'm trying to get a clear picture of the ServiceTracker. I've looked at 
>>>> some examples (e.g.  
>>>> <http://www.aqute.biz/Snippets/Tracker>http://www.aqute.biz/Snippets/Tracker
>>>>  <http://www.aqute.biz/Snippets/Tracker>), but they all seem rather 
>>>> complicated. I'm especially interested in knowing when I can assume a 
>>>> service object to exist.
>>>> 
>>>> It is clear to me from the specification that a service object is 
>>>> available (different from null) when the default implementation of 
>>>> addingService returns. So to avoid constantly calling 
>>>> myTracker.getService() (and check for null) whenever I want to invoke a 
>>>> method of the service object, I can derive my own ServiceTracker by 
>>>> overriding addingService (using the LogService as an example):
>>>> 
>>>>     @Override
>>>>     public LogService addingService(ServiceReference<LogService> 
>>>> reference) {
>>>>          myLogService = super.addingService(reference);
>>>>          // Start the thread(s) that refer to (use) myLogService
>>>>          return myLogService;
>>>>     }
>>>> 
>>>> ... and use myLogService until the service becomes unavailable (invalid).
>>>> 
>>>> It is less clear to me how to know when the service becomes unavailable. 
>>>> The specification says:
>>>> 
>>>>     removedService(ServiceReference,T) - Called whenever a tracked service 
>>>> is removed from the
>>>>     ServiceTracker object.
>>>> 
>>>> IMHO "is removed" is a bit unspecific (before/after?). However, I found in 
>>>> the Apache Felix implementation (which isn't a specification, of course) 
>>>> that removedService is invoked while handling the UNREGISTERING event:
>>>> 
>>>>     UNREGISTERING - A service object is in the process of being 
>>>> unregistered. This event is synchro-
>>>>     nously delivered before the service object has completed 
>>>> unregistering. That is, during the deliv-
>>>>     ery of this event, the service object is still valid.
>>>> 
>>>> So I should be on the safe side if I also override removedService:
>>>> 
>>>>     @Override
>>>>     public void removedService(ServiceReference<LogService> reference,
>>>>                                LogService service) {
>>>>         // Interrupt and join the thread(s) that refer to (use) 
>>>> myLogService
>>>>         myLogService = null;
>>>>         super.removedService(reference, service);
>>>>     }
>>>> 
>>>> Doing it this way, using myLogService in the thread(s) started in 
>>>> addingService and stopped in in removeService should be safe, right?
>>>> 
>>>>  - Michael
>>>>  
>>>> _______________________________________________
>>>> OSGi Developer Mail List
>>>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>
>>>> https://mail.osgi.org/mailman/listinfo/osgi-dev 
>>>> <https://mail.osgi.org/mailman/listinfo/osgi-dev>
>>>> 
>>>> 
>>>> _______________________________________________
>>>> OSGi Developer Mail List
>>>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>
>>>> https://mail.osgi.org/mailman/listinfo/osgi-dev 
>>>> <https://mail.osgi.org/mailman/listinfo/osgi-dev>
>>> _______________________________________________
>>> OSGi Developer Mail List
>>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>
>>> https://mail.osgi.org/mailman/listinfo/osgi-dev 
>>> <https://mail.osgi.org/mailman/listinfo/osgi-dev>
>> 
>> 
>> _______________________________________________
>> OSGi Developer Mail List
>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>
>> https://mail.osgi.org/mailman/listinfo/osgi-dev 
>> <https://mail.osgi.org/mailman/listinfo/osgi-dev>
> _______________________________________________
> OSGi Developer Mail List
> osgi-dev@mail.osgi.org
> https://mail.osgi.org/mailman/listinfo/osgi-dev

_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to