Book Image

Eclipse Plug-in Development Beginner's Guide - Second Edition

By : Alex Blewitt
Book Image

Eclipse Plug-in Development Beginner's Guide - Second Edition

By: Alex Blewitt

Overview of this book

Eclipse is used by everyone from indie devs to NASA engineers. Its popularity is underpinned by its impressive plug-in ecosystem, which allows it to be extended to meet the needs of whoever is using it. This book shows you how to take full advantage of the Eclipse IDE by building your own useful plug-ins from start to finish. Taking you through the complete process of plug-in development, from packaging to automated testing and deployment, this book is a direct route to quicker, cleaner Java development. It may be for beginners, but we're confident that you'll develop new skills quickly. Pretty soon you'll feel like an expert, in complete control of your IDE. Don't let Eclipse define you - extend it with the plug-ins you need today for smarter, happier, and more effective development.
Table of Contents (24 chapters)
Eclipse Plug-in Development Beginner's Guide Second Edition
Credits
Foreword
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Index

Dynamic services


The OSGi specification defines four different layers:

  • Security Layer: In this layer, all actions are checked against a security permissions model

  • Module Layer: In this layer, modules are specified as bundles that have dependencies

  • Life Cycle Layer: This layer bundles coming and going and firing events

  • Service Layer: In this layer, dynamic services that come and go

The Services layer allows bundles to communicate by defining an API that can cross bundle layers. However, the services layer also allows the services to come and go dynamically, instead of being fixed at runtime.

This mechanism allows services to be exported over a network, and since the network can come and go (as can the remote endpoint) the OSGi services layer can replicate that same functionality.

Responding to services dynamically coming and going may add a slight difficulty to the client code, but it will be more robust in case of failure. The following sections will present different ways of achieving dynamism in services.

Resolving services each time

The easiest way of working with dynamic services is to list the services each time they are needed. The example so far uses this technique to allow different services to be contributed.

This technique can work if the list of services is infrequently needed. However each time the lookup is performed, there is a cost to the acquisition, which may not be desirable.

Using a ServiceTracker

The OSGi framework provides a ServiceTracker class which can be used to simplify the acquisition of one or more services in a standard way. Provided in the org.osgi.util.tracker package, the ServiceTracker class has a constructor that takes a class and a BundleContext object, along with an optional filter specification.

Tip

The ServiceTracker has an open method which must be called prior to use, as otherwise it will not return any services.

Add the package to the timezones bundle's manifest as an import:

Import-Package: org.osgi.util.tracker

Modify the TimeZonesFactory so that a ServiceTracker is acquired in a static initializer, and that open is called. This simplifies the use method to simply delegate to the service tracker:

public class TimeZonesFactory {
  private static final Bundle bundle =
    FrameworkUtil.getBundle(TimeZonesService.class);
  private static final BundleContext context =
    bundle.getBundleContext();
  private static final ServiceTracker<TimeZonesService,
    TimeZonesService> tracker = 
     new ServiceTracker<>(context,TimeZonesService.class, null);
  static {
    tracker.open(); // Remember to call this!
  }
  public static void use(Consumer<TimeZonesService> consumer) {
    consumer.accept(tracker.getService());
  }
}

The ServiceTracker also has a close method, which should be called when services are no longer required to be tracked.

Tip

Generally tying the service tracker's lifecycle to another lifecycle is more appropriate, as otherwise this can leak implementation.

Filtering services

The service tracker, as it is currently implemented, returns all compatible services that implement the interface (if true is passed to the open call, both compatible and incompatible services are returned; this should generally not be used).

It is also possible to use a filter to restrict the list of services that are returned. OSGi filters are specified using the LDAP filter syntax, which uses prefix notation and parentheses to group elements. Here is how to read it:

  • A and B: (&(A)(B))

  • A or B: (|(A)(B))

  • Not A: (!(A))

  • A equals B: (A=B)

  • A contains B: (A=*B*)

These can be nested to form complex queries.

The services command in the Equinox console allows a filter to be evaluated. Each service is published into the registry, and the filter objectClass= allows services matching a particular interface to be found, as was done earlier in the chapter:

osgi> services "(objectClass=*.TimeZonesService)"
{com.packtpub.e4.timezones.TimeZonesService}={service.ranking=2,
 component.name=TimeZonesProvider, component.id=0, service.id=48}

It's possible to filter on other properties as well. For example, DS registers a component.id property with a service, so this can be used to create a filter for just DS registered components:

osgi> services "(&(objectClass=*.TimeZonesService)(component.id=*))"
{com.packtpub.e4.timezones.TimeZonesService}={service.ranking=2,
 component.name=TimeZonesProvider, component.id=0, service.id=48}

This looks for services ending in TimeZonesService and which have a value for the component.id property.

Filters can be included in the ServiceTracker to ensure that only desired services are picked up. For example, to include only services that aren't registered by DS, the following can be coded into the ServiceTracker:

Filter filter = context.createFilter(
 "(&(objectClass=*.TimeZonesService)(!(component.id=*)))");
st = new ServiceTracker<TimeZonesService, TimeZonesService>(
 context, filter, null);
st.open();

Note

This may be useful to enable debugging; the filter could be overridden by a system property, for example. Note that the createFilter method throws a checked syntax exception if it is invalid, which must be handled in the code.

Obtaining a BundleContext without using an activator

Since the ServiceTracker needs the BundleContext to register a listener, it is conventional to set up a BundleActivator for the sole purpose of acquiring a BundleContext.

Since this incurs a performance penalty, using a different mechanism to acquire the context will speed the start-up process. Fortunately there is a class, FrameworkUtil, that can be used to acquire a Bundle for any given class, and from there, the BundleContext. This allows the implementation of the TimeZonesActivator to be removed:

BundleContext context = FrameworkUtil.
  getBundle(TimeZonesFactory.class).getBundleContext();

Using this mechanism adds no performance penalty and should be used in favor of a global static instance to the BundleContext. It also potentially allows the bundle's activator to be removed from the bundle.

Note

If the bundle is not started, it will not have a BundleContext and so the returned value here may be null. Code should defensively handle this case.

Dependent Services

It is fairly common that an OSGi service depends on other OSGi services. As such it can help if the services are set up and made available when the bundles are available. The Declarative Services approach can be used to register services on demand when the requirements are satisfied.

For DS, when the cardinality of the relationship is not optional (in other words, the relationship is 1..1 or 1..n), then the service won't be started until the required dependent services are available. For example, a menu service may not be required until the graphical user interface service is present; and services that wish to contribute to the menu service won't be able to work until the menu service is present.

Delaying the creation of the services until they are needed will result in shorter start-up times of the application.