Given only a single service, your code would work between the one call to addingService and, later, the one call to removedService. But in the presence of multiple services, your code falls apart as described by Tim.
 
In general, there can be multiple services and their life cycles are fully asynchronous. So multiple threads can be calling a ServiceTracker to track and untrack services. This is why it is recommended to use DS since it was (starting in Release 4) designed to vastly simplify the use of services.
 
Since this is just an academic exercise, I hope you can appreciate the asynchronous and dynamic life cycle of services and why something like DS is a much better way to use services in an application. You let the DS runtime handle all the thorny issues. But DS cannot handle all possible service usage needs. To keeps its design manageable, there are certain use cases it does not support. So then you do fall back to ServiceTracker and use its methods to get the tracked services. But it is almost never a good idea to try and cache the service yourself as you show in your example code for the reasons Tim outlined.
 
--

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 Developer Mail List <osgi-dev@mail.osgi.org>
Cc:
Subject: Re: [osgi-dev] Clarify usage of ServiceTracker
Date: Fri, Apr 15, 2016 7:11 AM
 
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> 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> 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
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
 
 
 
_______________________________________________
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
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to