A Comparison of Eclipse Extensions and OSGi Services
Since Eclipse adopted the OSGi runtime in version 3.0, there has been some
tension between the Extension Registry, which has been a feature of Eclipse
from its beginning, and the Service Layer, which came from OSGi and pre-
existed the involvement of Eclipse. The cause of the tension is that these
two models overlap somewhat, and because they are both intended to solve
very similar problems. However “the Devil is in the details”, and these two
models are different enough to make it impractical for them to be merged.
Therefore developers of Eclipse plugins and RCP applications need to make a
choice between the two.
Until very recently, the choice for Eclipse developers has been very clear:
the Extension Registry is still used in almost all plugins and RCP
applications, to the exclusion of OSGi Services. This is reinforced by the
tooling that is available in Eclipse PDE, which provides lots of support
for extensions and none for services.
However, recent developments in OSGi have further blurred the
boundaries between the two models, and the tools are evolving too. This
leads to the inevitable question: “which one is best”?
Structure of the article
This article is intended primarily for Eclipse developers who are
reasonably familiar with the “Eclipse Way” of plug-in development with
extension points and extensions, but do not have much experience of
OSGi services. As such, there is not a lot of discussion of how
extensions work or why they exist. The section that follows is just a
brief overview.
After that, we discuss OSGi services in a little more depth, including
some example code. We talk about the issues that Eclipse
developers might come across when getting to grips with services. We
will also discuss some of the newer facilities available in OSGi which
may help to overcome those issues.
Finally we wrap up with a comparison grid and a conclusion with some
recommendations and further reading.
The Eclipse Extension Registry
The Extension Registry is a place where extension points are matched
up with extensions. An extension point is a declaration made by a
plug-in to say that it is open to being extended with new functionality
in a particular way. It is found in the plugin.xml
file for the
plug-in, and it consists of:
-
An extension point ID, which consists of a “namespace” and suffix. The
namespace is usually the ID of the plugin in which the extension point
is defined, but it need not be. This is different from an “XML namespace”.
-
A human-readable name.
-
An XML schema document, which defines the structure of the meta-data
that extensions are required to supply.
An extension is the flip-side of the coin. It is a declaration by a
plug-in to say that it provides functionality to extend another
plug-in. It is also found in the plugin.xml
and it specifies:
-
The ID of the extension point.
-
An XML node, which must comply with the schema specified by the
extension point.
After a plug-in is installed and enters the RESOLVED
state, Eclipse
queries it for contributions to the extension registry. A plug-in can
contribute either extensions or extension points, or both. The result
of the scan is cached between runs of Eclipse, so the only time that
all of the plug-ins are scanned is when Eclipse is started for the
first time, or when your run it with the -clean command line switch.
At this point, Eclipse has done very little work. You could think of
the registry as being just like a pair of HashMap
s, that allow
Eclipse to lookup either extensions or extension points by their ID. So
far, we have simply dropped a few items into the HashMap
s.
Some time later, a plug-in may decide to query the registry. Usually
this is done by the definer of an extension point. Essentially
that plug-in asks “who has declared that
they have extensions for me?” The registry API provides the plug-in
with a snapshot of all the currently installed extensions for the ID
that was queried, as an array of data structures that closely resemble
the actual XML nodes given by the extension. And that is (almost) the
whole story for the extension registry: it enables plug-ins to search
for meta-data declared by plug-ins. The key thing to notice here
is that no Java classes in the extension plug-in have yet been loaded.
We have merely loaded some XML data; this takes far less time and
memory than creating a ClassLoader and using it to load code.
Of course Java code has to be loaded at some point. When does that
happen? It depends on the definer of the extension point. So far it has
queried the extension registry and obtained meta-data, but that
meta-data often contains the name of a Java class. In order to make use
of that class, the plug-in asks the extension registry to load and
instantiate the class.
So it is up to the definer of the extension point to decide when the
code in the extensions is loaded. It’s perfectly possible to
immediately load all of the classes; however this usually isn’t
necessary. Typically the meta-data is sufficient to create some kind
of placeholder, and then the actual class is only loaded when the user
expresses some interest in it.
For example, the org.eclipse.ui.actionSets
extension point can be
used to add buttons to the application toolbar. Clicking a button
executes a task of some kind, which is implemented as Java code.
However, until the button is clicked it consists solely of an icon and
a label. Therefore the meta-data for the extension point includes the
text for the label and the resource path of the icon. No code is loaded
until the user has expressed an interest in the button, i.e. by
actually clicking on it.
Figure 1 shows a UML-style sequence diagram of how extension point
definers and extension providers interact with the extension registry.
Figure 1: Extensions. Click on image for full size
Another interesting aspect of extensions is that they needn’t result in any
code being loaded at all. It is entirely up to the definer of the extension
point. There are many extension points in the core RCP plug-ins that do not
require a class
attribute. For example, all of the Help documentation in
Eclipse is contributed through extensions, and there is no Java code
involved — just HTML files.
The points to take away from this are:
-
extensions are declarations
-
plug-ins consult extensions to create placeholders for functionality
-
the functionality behind the placeholder is realized when the extension
point definer chooses, which enables lazy loading and smaller memory usage
-
extensions need not be associated with executable Java code
OSGi Services
By contrast, an OSGi Service is a Java object (or, if you prefer, a
POJO). It is registered in the Service Registry under the name of a Java
interface.
Unlike extensions, which are scanned and registered during start-up,
services are not automatically registered. To register a service, a bundle
must first create an object, and then it must make a call to the OSGi API
to register the object as a service. For example suppose we have a service
that is defined by the interface org.example.ICustomerLookup
and a class
that implements it called DbCustomerLookup
. We could register it as
follows:
public void start(BundleContext context) {
// Create the service object
DbCustomerLookup lookup =
new DbCustomerLookup("jdbc:mysql:localhost/customers");
// Create the properties to register with the service
Dictionary properties = new Hashtable();
properties.put("dbname", "local");
// Register the service
context.registerService(ICustomerLookup.class.getName(), lookup,
properties);
}
There are a number of ways for a bundle to lookup and use a service
from another bundle. This is slightly harder because the service layer
in OSGi is implicitly dynamic — we have to be prepared for the fact
that services can come and go at any time. Therefore the familiar
technique of looking up a dependency once and caching it will not work
in OSGi. One solution is to lookup the service every time we need to
access it, but as the following code shows this can quickly become
cumbersome:
public void start(BundleContext context) {
this.context = context;
}
public String getCustomerName(long id) {
ServiceReference ref = context.getServiceReference(
ICustomerLookup.class.getName());
if(ref != null) {
ICustomerLookup lookup = (ICustomerLookup)
context.getService(ref);
if(lookup != null) {
Customer cust = lookup.findById(id);
context.ungetService(ref);
return cust.getName();
}
}
// Couldn't get name -- service not available
return null;
}
Figure 2 shows a sequence diagram of the steps involved in registering
and consuming a service.
Figure 2: Services. Click on image for full size
Because doing this is so awkward, there is a utility class that can be
used to make things much easier. It is called ServiceTracker
and can be used as follows:
public void start(BundleContext context) {
this.custLookupTracker = new ServiceTracker(context,
org.example.ICustomerLookup.class.getName(), null);
this.custLookupTracker.open();
}
public String getCustomerName(long id) {
ICustomerLookup lookup = (ICustomerLookup)
this.custLookupTracker.getService();
if(lookup == null) {
return null; // Alternatively might throw an exception
} else {
Customer cust = lookup.findById(id);
return cust.getName();
}
}
ServiceTracker
also makes other types of interaction with
services easier, such as keeping track of when the service is registered
and unregistered. Doing this with the lower level APIs such as
ServiceListener
can be quite error-prone, so it is
recommended to always use ServiceTracker
to consume
services.
Notice that in both registering services and accessing services, the
interaction with OSGi is through an instance of the BundleContext
interface. In fact, the BundleContext
interface is the only way for
bundles to interact with the facilities of the OSGi runtime.
Furthermore, the only way we can get a BundleContext
is to wait for
the OSGi runtime to give one to us — we do this by providing a
BundleActivator
for our bundle. When the bundle is started, the
runtime calls the start()
method of our BundleActivator
and
supplies a BundleContext
instance. Before our bundle can be started,
it must be loaded. Therefore, until the bundle is loaded, there can be
no services registered. Suppose, then, that all the functionality of
our application is implemented as OSGi services. Which bundles do we
start?
There are three options. First, we could start none of them. This
clearly doesn’t work. Second, we could start all of them. This works,
but it would result in slow startup times, and our application would
use more memory than it needs to get a particular job done. The third
option is to know in advance which bundles need to be started, and only
start those. This option is very hard to achieve when services are
registered by programmatic code, as we have just described. The problem
is that a bundle has total freedom to register a service whenever it
chooses, or alternatively it might not register a service at all
because of certain preconditions. For example, suppose we have service
A and service B. it is very common for service B to have a dependency
on service A, i.e. service B uses the facilities of A to get its own
work done. To support this scenario, the bundle that registers service
B can install a listener, and ensure that service B registers only when
service A becomes available. This is just one scenario — there are
many more possibilities. A bundle might even decide only to register a
service when started on Fridays between 1PM and 2PM. Because control is
in the hands of arbitrary programmatic logic, the possibilities are
infinite and unpredictable.
So, because bundles have such flexibility in registering services, it
is really only possible to build an application out of services when
the preconditions of all the services are carefully documented. This is
clearly impractical for applications like the Eclipse IDE, where the
base application is extended by plug-ins from third parties. In fact,
it can quickly become impractical for large applications, even those
built by a single team. But Declarative Services can help (see next
section).
So do services have any advantages over extensions? In my opinion, yes,
they do have one significant advantage: they are very much better at
dealing with dynamic installation and uninstallation than extensions.
This is not to say that extensions do not handle dynamic behaviour.
They do, but some people feel the API is complex and difficult to work
with, and the impression when working with it is that the dynamic
aspects were bolted on top of an existing API rather than being part of
the API from the start. In fact that’s exactly what happened. Before
Eclipse 3.0 the extensions API was not dynamic, and the core of the API
has not changed since then. By contrast the API for working with
services is inherently dynamic. This is sometimes inconvenient — one
is forced to think about the dynamic aspects even when one doesn’t want
to! — but in truth this is good discipline, since the real world is
intrinsically dynamic.
There are some scenarios where the advantages of services outweigh the
problems related to activation. Take for example servers. In an ideal
world, servers should almost never be shut down, but keep going and
going until the hardware they run on wears out. In this context, we
might as well start all bundles when the server comes up. It may take
longer to start, but what do we care about a few extra minutes in
start-up time if the server is going to be running for months or years?
Actually developers care, because they need to quickly deploy and test
new versions of their code to development servers. However, here again
the dynamic qualities of services can help, because new versions can be
deployed to a running server wihout needing to restart the whole thing.
Another key difference is in the way that services and extensions interact
with their consumers. The extension model is essentially “one-to-many”,
meaning that an extension is explicitly intended for consumption by one
particular plug-in; i.e. the definer of the extension point. On the other
hand services use a “many-to-many” model, meaning that a single service
can have many consumers. Services are registered so that anybody can find
them and use them. For example, imagine the scenario of publish/subscribe
messaging. Each publisher could be matched with multiple subscribers, and
each subscriber could receive messages from multiple publishers.
So what we really want is something that combines the advantages of
both extensions and services. Something that is implicitly dynamic like
services, but loaded “on demand” like extensions. Ideally, something
that can also simplify the code that application developers must write.
The first steps towards such a solution were taken by Humberto
Cervantes and Richard S. Hall in their Service Binder library. This
attempted to bring a declarative flavour to OSGi services, along with
automating the wiring up of dependencies between services. Service
Binder, together with some input from the Eclipse developers (who by
this point had become heavily involved in OSGi) evolved into a
framework called Declarative Services (DS), developed within the
scope of OSGi Release 4. An implementation of Declarative Services
can be found in the Equinox project. Equinox is an implementation of
OSGi Release 4 and it has been at the core of Eclipse since version 3.0.
Declarative Services
The key observation that led to Service Binder and Declarative Services
was that there
are actually two separate responsibilities involved in offering a
service. Firstly somebody has to create an implementation of a service.
Secondly somebody has to publish the service in the service registry.
In the traditional services model we described above, those two
somebodies were usually the same – i.e. the bundle that implements the
service also registers it. However if we separate the two
responsibilities, then we can achieve something really powerful.
That is what Declarative Services does. The responsibility for implementing services
remains with the bundles as before; however the responsibility for
registering services is subsumed by a special bundle called the Service
Component Runtime or SCR. So instead of each bundle needing supply code
to register its services, it simply allows the SCR to register those
services on its behalf.
Naturally the SCR needs some way to know about services which it is
supposed to register. As the name “Declarative Services” suggests, this
information is provided declaratively. In fact, we’ve come full circle
back to XML files. A bundle containing services that it wants the SCR
to manage on its behalf contains one or more XML files, with each file
representing a “Service Component”. The SCR scans bundles for these XML
files, and registers the services described therein. As opposed to
extensions (where there is a single XML file per bundle and it is
always called plugin.xml
), there can be an arbitrary number of XML
files per bundle, with arbitrary names. Therefore the files have to be
listed in the bundle manifest under a new Service-Component
in order
for SCR to find them.
Now, the scenario above is still not “on demand”. To support lazy
registration, Declarative Services has the notion of “delayed” services. When the SCR
registers a delayed service, it creates a proxy object to act as a
placeholder, and registers that in the service registry. From the point of
view of a consumer, the service is now available for use, however the
bundle containing the real service implementation has not been started yet.
Then when a consumer tries to actually use the service, the SCR detects
this, and asks the OSGi runtime to fully load and activate the bundle. It
then subtitutes the proxy for the real service. A key difference between
extensions and Declarative Services is that, for the consumer, DS implements laziness
transparently, i.e. the code that uses a service need not know or care that
the service is temporarily only represented as a placeholder. In contrast,
the consumer of an extension must create and manage the placeholder
explicitly, and choose when to swap the placeholder for the real
implementation.
Figure 3 shows the sequence diagram for Declarative Services. Notice that all of the
steps from the vanilla OSGi services diagram are still present. DS
refines the services model, rather than replacing it.
Figure 3: Declarative Services. Click on image for full size
Another problem we identified with services — namely that programmatic
code has too much flexibility with regard to whether and when it
registers services — is also addressed by Declarative Services. The logic controlling
the registration of services is specified via declarations, which are
easy to access and reason about. This makes it much easier for
adminstrators to, say, work out why a particular service won’t come up,
and then remedy the situation.
Composing Services
One very interesting and powerful feature of Declarative Services is the ease with which
one can compose services.
As mentioned earlier, it is quite common to find two services, A and B,
where service B depends on service A. That is, when a method on service
B is called, it may make one or more calls to service A to achieve its
end result. Next suppose that service C depends on service B: we have
started to create a graph of dependent services. Declarative Services
makes this graph easy to construct. In the XML file for service B, we
simply create a reference
to service A – then SCR will wire up the
dependency whenever it can be satisfied.
Note that, services being dynamic, there are some interesting questions
that arise. For example, what happens if service A goes away – what
should we do with service B? Should we kill it, or should it continue
working anyway? The answer to this depends on whether the dependency is
optional or mandatory. Next, what happens when there are twenty
instances of A? Does B want them all, or just one? If just one, which
one do we choose? Finally, suppose B is bound to a particular instance
of A, and that instance goes away, but there is already a suitable
subsitute available (e.g. one of the remaining 19 instances). Should we
switch B to use the new A, or do we need to kill B and then create a
new B pointing at the new A?
These are all valid questions, and the answer in each case is that it
depends on our requirements. Therefore DS provides control in the three
dimensions involved:
- Optional vs mandatory.
- Unary vs multiple.
- Static vs dynamic.
Combining the first two axes gives the familar four choices of 0..1
(optional + unary), 1..1
(mandatory + unary), 1..n
(mandatory +
multiple) and 0..n
(optional + multiple). The third axis is less
familiar. A dependency is dynamic if it can be hot-swapped, i.e. if
service B can be supplied subtitute instances of A. Sometime this isn’t
possible; for example A might be stateful, or B may simply not be able
to handle substitution because of the way it was programmed. We say
that the dependency is static – we must destroy B and create a new
instance of it.
All of the above actions naturally can cause follow-on effects in the
larger graph. If service C has a static, mandatory dependency on B, and
service B has a static, mandatory dependency on A, then every time an
instance of A goes away we find that both B and C must be stopped and
restarted. When the graph is large, we find that a single service
restart can cause an avalanche of restarts across the system. However
the avalanche stops when it hits an optional or dynamic dependency. So
for the most scalable solution, it is good practice to make your
dependencies optional and dynamic whenever possible.
Note that all of the above can be achieved with OSGi services. DS is
built on services, after all. But the code required to handle all of
the scenarios and their corner cases is complex and error-prone. By
contrast, DS lets you specify declaratively what the behaviour should
be, and is tested thoroughly for the corner cases.
Making a Choice
It might seem that Declarative Services can do everything that extensions can do, and in
addition is better at dealing with dynamics. Also since DS is a part of
the OSGi specification whereas extensions are an external extra (albeit
part of every Eclipse download), there should be a natural desire to
use DS.
Unfortunately this is not quite the case yet. The “delayed” services
feature requires some small tweaks to the basic Equinox runtime, and
these tweaks have not been implemented yet in the released version 3.2.
They do not appear at all yet (at the time of writing) in the other
open source OSGi runtimes, Apache Felix and Knopflerfish. They do
appear in the latest milestone releases of Equinox 3.3, but you may
feel it is risky to use a milestone release in a production application.
Also, there is simply no tooling available in the Eclipse Plugin
Development environment (PDE) at the moment for Declarative Services. Building bundles
with DS requires maintaining XML files according to a special schema,
keeping a list of those XML files in the bundle manifest and ensuring
it remains synchronised, and keeping XML files up to date with changes
in the Java code as the class and method names evolve. By contrast PDE
provides lots of support for graphical editing of extensions and
extension points, refactoring support and so on. So the current tooling
in Eclipse strongly encourages you to use extensions rather than DS.
Finally, DS is not the only solution to the problems we described with
services. Two alternatives from Apache Felix are the Felix Dependency
Manager, and a library called iPOJO. However I believe that the
stiffest competition will come from the Spring Framework.
In 2006, Spring continued to spread its influence to almost all corners
of the Java world. Rod Johnson (the father of Spring) declared that
2006 was the year Spring became ubiquitous. I think Rod was
exaggerating, but only slightly: in all likelihood 2007 will be the
year.
What does Spring have to do with Eclipse and OSGi? Well, just as Spring
2.0 was being prepared for final release, several of the core
developers working on Spring started to take notice of OSGi, and they
liked what they saw. In particular they liked services, which they
realized were similar in many ways to the “beans” that Spring wires
together. However OSGi is dynamic, whereas Spring is largely static.
Another thing that the Spring developers noticed was that the
programming model for services was somewhat awkward to use, and tended
to involve a fair amount of repetitive boilerplate code. They
reckoned that if Spring and OSGi could work together, then the
resulting platform would benefit from OSGi’s strength in dynamics and
Spring’s strength in simplified programming models and reduced
boilerplate. Therefore a decision was made to build support for OSGi in
Spring 2.1.
There is, frankly, a very high degree of overlap between Spring’s OSGi
support and Declarative Services. The Spring developers looked at Declarative Services
and decided that Spring already does a lot of what DS does, and does it
better. This point is debatable. But the upshot is, the long term
success of DS is by no means guaranteed. Despite being the de jure
standard from the OSGi specifications, developers tend to favour a de
facto standard — which may well end up being Spring. In fact, recent
suggestions from Peter Kriens (the OSGi Evangelist) indicate that he is
in favour of Spring-OSGi becoming part of the OSGi specification in
Release 5.
By the way, it’s important to note that (just like DS) Spring’s OSGi
support will require the same runtime tweaks to allow for on-demand
usage. Also Spring-OSGi is in its infancy, and is absolutely not
suitable for production usage at the moment. Declarative Services is somewhat more
mature, but the implementation available in Eclipse is still a little
buggy. In part this is due to a chicken-and-egg problem — if more
developers were using DS and reporting bugs, then DS would become more
stable. But until it is stable, many developers hold back on using it.
So, will DS or even Spring-OSGi someday replace the use of extensions
in Eclipse? It’s conceivable, yes. But it’s highly unlikely to happen for a few years.
By that time I believe that Eclipse will be widely
used both for server platforms as well as IDEs and GUIs, and with the
widened scope Eclipse may need a more flexible model than is provided
by extensions. However, extensions will certainly be around for a long
time yet.
So what should we do today? Should we start planning our migration from
extensions to Declarative Services or Spring-OSGi? Should we be worried about building
our applications with extensions, which may be heading towards
obsolescence?
No, not at all. If extensions are a good fit for your requirements, then
have no fear: use extensions. They are mature and will be supported for a
long time into the future. So does that mean you should avoid services, or
DS or Spring-OSGi? Also no. If they are a good fit for your requirements,
use them. Well, it would be prudent to wait for Spring-OSGi to mature, but
traditional services are very mature already, and DS is mature enough for early
adopters to start using.
Alas, the qualification “if they are a good fit for your requirements”
is not so easy to evaluate. In practise, you may find your requirements
could be satisfied by both extensions and services. The hard part is
deciding which is the best fit. Here are a few guidelines.
For people buiding plug-ins that will be installed into a larger
application such as the Eclipse IDE, the choice is extremely clear: use
extensions. That is simply the only way at the moment, since you have
to use what the host application uses. If you write your plug-ins using
only services, there will simply be nobody listening at the other end.
If you are building an RCP application, the choice is still fairly
clear: use extensions, mostly. It is not impossible to abandon
extension and use services instead, especially if you have control over
the whole application. But by doing so you will be sacrificing a lot of
the existing framework of RCP, which expects you to be using
extensions. You will have to reimplement the entire infrastructure of
views, perspectives, editors, action sets etc. Also, you will have
issues with activation, and you will not benefit from lazy loading
unless you go with Declarative Services (or Spring-OSGi) plus the latest milestones of
Eclipse 3.3.
On the other hand, you might find some parts of your application work well
with services. If that’s the case then by all means use them in addition to
extensions. But be sure to understand the activation issues, and have a
plan for ensuring the plug-ins that need to be started actually get
started.
If you’re using Eclipse or OSGi to develop something other than an RCP
application — such as a server, or perhaps a batch process — then
services may be a better bet. In these kinds of applications,
hot-swapping functionality without downtime can be more important than
fast start-up. Again, there is nothing to stop you using extensions,
but you will have to deal with the dynamic extensions API and its
warts.
Common Misconceptions
Before concuding, lets take a quick look at some common misconceptions.
The following arguments are sometimes used to justify a preference for
extensions or services:
-
“Extensions are not pure OSGi”. This is not true. The Extension
Registry is implemented by the bundle org.eclipse.equinox.registry
,
which depends on org.eclipse.equinox.common
. These bundles are
implemented with pure OSGi code. There are no secret hooks into
specific features of Equinox (the Eclipse implementation of OSGi) and
therefore there is no reason, at least in theory, that extensions
cannot be used on other OSGi runtimes such as Apache Felix or
Knopflerfish.
-
“Services do not have meta-data.” This is also wrong. Services can
be published with any number of arbitrary properties, and these
properties can be easily accessed by service consumers. Extensions
perhaps provide more flexible meta-data than services (i.e. an XML node
versus a list of name/value pairs) but this is unlikely to be a real
differentiator.
-
“Extensions are a legacy of Eclipse 2.0”. This is true in a sense,
but “legacy” can imply “obsolete”. As has been argued, extensions are
anything but obsolete, and will not be for some time to come.
Comparison Matrix
|
Extensions |
Services |
Declarative Services |
Spring-OSGi |
What are registered? |
XML declarations, optionally containing class names. |
Java objects. |
Placeholder proxies for Java objects, replaced by real Java objects on
first use. |
Placeholder proxies for Java objects, replaced by real Java objects on
first use. |
How are they registered? |
All <extension> nodes in the plugin.xml
are automatically registered. |
Through the BundleContext API |
All service component XML files declared through the Service-
Component manifest entry are automatically registered. |
All Spring XML files contained in the META-INF/spring
folder of a bundle, or declared through the Spring-Context
|
How are they consumed? |
Queried by extension point ID. Registry returns all entries, consumer
must iterate them to find which one(s) it wants. |
Queried by interface name, plus a property filter. Usually the filter
is specific enough to ensure just one match. |
Internally uses same query over interface name plus filter, but the
query is performed within SCR. Matching services are supplied to consumer
code via JavaBean-style setter methods. |
Same as for DS, but can additionally supply matching services via
constructor arguments. |
What is the cardinality? |
Conventionally, one to many. One extension point has many extensions, but each
extension has exacly one extension point. However another plug-in can query
extension points that it doesn’t own. |
Many to many. One service can be used by multiple consumers, and one
consumer can use many services. |
Same as for services |
Same as for services |
When are they loaded? |
Extension declarations are loaded during start-up, or when a new plug-in
is installed. Classes named in the extension are loaded lazily, i.e.
only when needed. |
The class that implements a service must be loaded and instantiated
before the service can be registered. This happens only when the bundle is
explicitly started. |
For delayed services, the SCR registers a placeholder proxy service
immediately when a DS-enabled bundle is resolved. The real class is loaded
and instantiated when the service is first used. |
Same as for DS. |
How is dynamic installation/uninstallation dealt with? |
Either query on each use or track with ExtensionTracker . |
Either query on each use or track with ServiceTracker . |
SCR calls methods on the component to supply it with the latest
available matching service. It can also call an unset method when the
service becomes unavailable. |
Similarly to DS, supplies latest matching service through setter method
on the bean. |
Can caching references to extensions/services cause problems? |
Yes, and as a legacy of pre-OSGi usage, some plug-ins still do this. |
Yes, but this is strongly discouraged by the specification, and in practice happens
rarely. |
No, SCR does not do this. |
No, Spring-OSGi does not do this. |
Conclusion
In this article I have described in general terms some of the strengths and
weaknesses of Eclipse-style extensions and OSGi-style services. However I
would not wish my readers to take away a simplistic message that
“extensions aren’t dynamic” or “services can’t be used in RCP
applications.” I’m afraid the issues are too subtle for that, and there is
no substitute for making your own evaluation in the context of your
requirements. To help you do that, I leave you with some references to
further information on each of the subjects discussed.
About the Author
Neil is a Java developer and consultant specialising in Eclipse RCP and
OSGi, in particular their use in large enterprise settings. Recently he
has helped a number of financial institutions with their adoption of
RCP and evangelises the use of the RCP/OSGi technology platform for
both client and server-side development.