Package org.eclipse.core.runtime

# Class ServiceCaller<S>

java.lang.Object
org.eclipse.core.runtime.ServiceCaller<S>

Type Parameters:
  `S` - the service type for this caller

public class ServiceCaller<S> extends Object
`ServiceCaller` provides functional methods for invoking OSGi services in two different ways

- Single invocations which happen only once or very rarely. In this case, maintaining a cache of the service is not worth the overhead.
- Multiple invocations that happen often and rapidly. In this case, maintaining a cache of the service is worth the overhead.

For single invocations of a service the static method `callOnce(Class, Class, Consumer)` can be used. This method will wrap a call to the consumer of the service with the necessary OSGi service registry calls to ensure the service exists and will do the proper get and release service operations surround the calls to the service. By wrapping a call around the service we can ensure that it is correctly released after use.

Single invocation example:

     ServiceCaller.callOnce(MyClass.class, ILog.class, (logger) -> logger.info("All systems go!"));

Note that it is generally more efficient to use a long-running service utility, such as `ServiceTracker` or declarative services, but there are cases where a single one-shot lookup is preferable, especially if the service is not required after use. Examples might include logging unlikely conditions or processing debug options that are only read once.

This allows boilerplate code to be reduced at call sites, which would otherwise have to do something like:

     Bundle bundle = FrameworkUtil.getBundle(BadExample.class);
     BundleContext context = bundle == null ? null : bundle.getBundleContext();
     ServiceReference<Service> reference = context == null ? null : context.getServiceReference(serviceType);
     try {
            Service service = reference == null ? null : context.getService(reference);
            if (service != null)
                    consumer.accept(service);
     } finally {
            context.ungetService(reference);
     }

For cases where a service is used much more often a `ServiceCaller` instance can be used to cache and track the available service. This may be useful for cases that cannot use declarative services and that want to avoid using something like a `ServiceTracker` that does not easily allow for lazy instantiation of the service instance. For example, if logging is used more often then something like the following could be used:

     static final ServiceCaller<ILog> log = new ServiceCaller(MyClass.class, ILog.class);

     static void info(String msg) {
            log.call(logger -> logger.info(msg));
     }

Note that this class is intended for simple service usage patterns only. More advanced cases should use other mechanisms such as the `ServiceTracker` or declarative services.

Since:
  3.13

## Constructor Summary

Constructors
Constructor
Description
`ServiceCaller(Class<?> caller, Class<S> serviceType)`
Creates a `ServiceCaller` instance for invoking an OSGi service many times with a consumer function.
`ServiceCaller(Class<?> caller, Class<S> serviceType, String filter)`
Creates a `ServiceCaller` instance for invoking an OSGi service many times with a consumer function.

## Method Summary

Modifier and Type
Method
Description
`boolean`
`call(Consumer<S> consumer)`
Calls an OSGi service by dynamically looking it up and passing it to the given consumer.
`static <S> boolean`
`callOnce(Class<?> caller, Class<S> serviceType, String filter, Consumer<S> consumer)`
As `callOnce(Class, Class, Consumer)` with an additional OSGi filter.
`static <S> boolean`
`callOnce(Class<?> caller, Class<S> serviceType, Consumer<S> consumer)`
Calls an OSGi service by dynamically looking it up and passing it to the given consumer.
`Optional<S>`
`current()`
Return the currently available service.
`void`
`unget()`
Releases the cached service object, if it exists.

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Constructor Details

### ServiceCaller

public ServiceCaller(Class<?> caller, Class<S> serviceType)
Creates a `ServiceCaller` instance for invoking an OSGi service many times with a consumer function.

Parameters:
  `caller` - a class from the bundle that will consume the service
  `serviceType` - the OSGi service type to look up
Throws:
  `NullPointerException` - if any of the parameters are `null`
  `IllegalStateException` - if the bundle associated with the caller class cannot be determined

### ServiceCaller

public ServiceCaller(Class<?> caller, Class<S> serviceType, String filter)
Creates a `ServiceCaller` instance for invoking an OSGi service many times with a consumer function.

Parameters:
  `caller` - a class from the bundle that will consume the service
  `serviceType` - the OSGi service type to look up
  `filter` - the service filter used to look up the service. May be `null`.
Throws:
  `NullPointerException` - if any of the parameters are `null`
  `IllegalStateException` - if the bundle associated with the caller class cannot be determined

## Method Details

### callOnce

public static <S> boolean callOnce(Class<?> caller, Class<S> serviceType, Consumer<S> consumer)
Calls an OSGi service by dynamically looking it up and passing it to the given consumer.

If not running under OSGi, the caller bundle is not active or the service is not available, return false. If the service is found, call the service and return true.

Any runtime exception thrown by the consumer is rethrown by this method. If the consumer throws a checked exception, it can be propagated using a sneakyThrow inside a try/catch block:

     callOnce(MyClass.class, Callable.class, (callable) -> {
       try {
         callable.call();
       } catch (Exception e) {
         sneakyThrow(e);
       }
     });
     ...
     @SuppressWarnings("unchecked")
     static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
       throw (E) e;
     }

Type Parameters:
  `S` - the OSGi service type to look up
Parameters:
  `caller` - a class from the bundle that will use service
  `serviceType` - the OSGi service type to look up
  `consumer` - the consumer of the OSGi service
Returns:
  true if the OSGi service was located and called successfully, false otherwise
Throws:
  `NullPointerException` - if any of the parameters are `null`
  `IllegalStateException` - if the bundle associated with the caller class cannot be determined

### callOnce

public static <S> boolean callOnce(Class<?> caller, Class<S> serviceType, String filter, Consumer<S> consumer)
As `callOnce(Class, Class, Consumer)` with an additional OSGi filter.

Type Parameters:
  `S` - the OSGi service type to look up
Parameters:
  `caller` - a class from the bundle that will use service
  `serviceType` - the OSGi service type to look up
  `consumer` - the consumer of the OSGi service
  `filter` - an OSGi filter to restrict the services found
Returns:
  true if the OSGi service was located and called successfully, false otherwise
Throws:
  `NullPointerException` - if any of the parameters are `null`
  `IllegalStateException` - if the bundle associated with the caller class cannot be determined

### call

public boolean call(Consumer<S> consumer)
Calls an OSGi service by dynamically looking it up and passing it to the given consumer. If not running under OSGi, the caller bundle is not active or the service is not available, return false. Any runtime exception thrown by the consumer is rethrown by this method. (For handling checked exceptions, see `callOnce(Class, Class, Consumer)` for a solution.) Subsequent calls to this method will attempt to reuse the previously acquired service instance until one of the following occurs:

- The `unget()` method is called.
- The service is unregistered.
- The service properties change such that this `ServiceCaller` filter no longer matches.
- The caller bundle is stopped.
- The service rankings have changed.

After one of these conditions occur subsequent calls to this method will try to acquire the another service instance.

Parameters:
  `consumer` - the consumer of the OSGi service
Returns:
  true if the OSGi service was located and called successfully, false otherwise

### current

public Optional<S> current()
Return the currently available service.

Returns:
  the currently available service or empty if the service cannot be found.

### unget

public void unget()
Releases the cached service object, if it exists. Another invocation of `call(Consumer)` will lazily get the service instance again and cache the new instance if found.