Thanks for pointing this out. Indeed, I didn't reply to this hint. I'm currently trying to (as fully as possible) understand what OSGi can do in my architecture. I'm not (yet) trying to solve an immediate implementation problem. From experience, I know that "high level APIs" are nice to work with, but -- in a complex project -- sooner or later you run into a problem where something cannot be solved using the high level API only. So before taking a decision on whether to use OSGi in my project or not, I want to get an as clear as possible picture. This also includes some understanding how things can be done using the "mid-level API" provided by the service tracker.
I haven't tracked down the specification history. But there must have been some time span in which OSGi had the ServiceTracker, but not yet DS. Maybe I didn't search with the right keywords, but somehow I failed to find convincing examples on the web how to model my use case (dependency on *required* service) using the ServiceTracker but without using "redundant code" (the check for null, sorry that I insist on this). I couldn't really clarify the issue from the specification and therefore, to make sure that I have understood things properly, I asked this question. Call it academic if you like, but I really want to know whether I can safely omit the check for null if my code runs between addingService and serviceRemoved. A positive answer will give me the feeling that I have understood things properly. Actually, I did consider BJ Hargrave's response a positive answer and the issue closed. But then Peter Kriens mail somehow obscured things again. - Michael Am 15.04.2016 um 12:24 schrieb Timothy Ward: > 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), 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: > > 1. 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. > 2. If multiple services are added then multiple sets of threads will > be started. > 3. If any service is unregistered then all threads are stopped. > 4. 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) > 5. 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 >> <mailto: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, 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 >>>>> >>>>> >>>>> >>>>> ----- Original message ----- >>>>> From: Michael Lipp <m...@mnl.de> >>>>> Sent by: osgi-dev-boun...@mail.osgi.org >>>>> To: 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), 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 >>>>> 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 <mailto: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 >> >> _______________________________________________ >> OSGi Developer Mail List >> osgi-dev@mail.osgi.org <mailto: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
_______________________________________________ OSGi Developer Mail List osgi-dev@mail.osgi.org https://mail.osgi.org/mailman/listinfo/osgi-dev