[resending due to bounces]
Notes from a face-to-face meeting between myself, Hal, and Andy Piper
in London last week (unfortunately Costin was unable to be there).
The behaviour of osgi:reference
===============================
* semantics of 0..1 cardinality
An osgi:reference with cardinality 0..1 is optional, creation of an
application context is not delayed until such a reference is
satisfied. The bean resulting from an osgi:reference declaration is a
proxy that tracks service(s) matching the reference criteria
(interface + filter + type compatibility).
Initially the proxy has no backing target service. When a matching
service is available (which may be immediately if one is present at
startup) the bind callback on any registered listener beans is
invoked, and the target of the proxy is set to be the matching
service. When several services meet the criteria, the backing service
is selected following the pre-existing OSGi rules: lowest service id.
If a target service is unregistered, then the unbind callback will be
invoked on any registered listeners. The proxy will attempt to locate
a replacement target service before returning from the unregister
event handler. If such a service is found, the bind callback will be
invoked on all registered listeners.
The proxy *does* follow the multiple-readers, single-writer locking
scheme previously outlined. This guarantees that under common
scenarios for enterprise Java applications updates can take place
under load with a guarantee that clients will not see a transient
failure. Common use cases might be updating a "service layer" bundle
providing application service objects (as OSGi services), or updating
a data-access bundle providing DAO services. For Spring applications,
the vast majority of published services *are* stateless and *can be*
transparently updated.
For the (much rarer in Spring-powered enterprise apps, but more
common in 'traditional' OSGi applications that may also want to use
the Spring-OSGi support) situation where the backing service is
stateful and cannot be transparently replaced, a client can implement
custom logic in the bind and unbind callbacks to manage
re-establishment of state or otherwise handle the service change.
When an operation is invoked on a service bean that has no backing
target service, the behaviour is as follows:
* wait for 'timeout' milliseconds for a matching service to be
registered. If one is registered select it as the new target
(invoking bind callbacks) and invoke it.
* If there is no timeout ("-1"), or a timeout is specified and the
timeout period expires, then service unavailable processing
begins.
- if the osgi:reference bean has a configured
service-unavaible-handler (attribute which refers to a bean
implementing the service interface) then the operation is
invoked on the handler bean and the return value from the handler
is returned to the caller
- if the osgi:reference bean does not have a configured
service-unavailable-handler then a ServiceUnavailableException
is thrown.
* semantics of 1..1 cardinality
The semantics of an osgi:reference with 1..1 cardinality are the same
as those given for 0..1, with the following exceptions:
* unless the wait-for-dependencies=false directive is specified in
the manifest, the creation of the application context will be
deferred until the reference can be satisfied.
* when the service backing a mandatory reference is unregistered,
and no replacement can be found, *no action is taken until an
operation is invoked on that service*. If/when the service is
invoked (and if there is still no matching backing service at that
point in time after any timeout period specified) then the
behaviour is as follows:
- if a service-unavailable-handler has been set then this
handler is invoked
- if no service-unavailable-handler has been set and the
mandatory-after-startup attribute has been set to "false"
(default=true) then a ServiceUnavailableException is thrown
- if no service-unavailable-handler has been set and
mandatory-after-startup is true then a
ServiceUnavailableException is thrown. In addition,
+ if the refresh-context-on-failure attribute is "true"
(default value) then the application context is 'refreshed'
(destroyed and then re-started when dependencies are
satisfied once more).
Note: a possible alternative to having a mandatory-on-startup
attribute is to introduce a fifth cardinality, somthing like
"1..1:0..1" which essentially means the same thing as
mandatory-after-startup=false
* semantics of 0..n cardinality
The bean resulting from an osgi:reference with cardinality 0..n is
of type Set. The members of the set are proxies that implement the
service interface and are backed by registered services matching the
reference criteria (one proxy for each unique matching service).
For each matching service added to the set, the bind callback is
invoked on all registered listeners. For each set member that is
subsequently unregistered, the unbind callback is invoked on all
registered listeners.
When an iterator over the Set is created, the iterator will iterate
over the set of proxies that were valid (had backing services) at
the point in time the iterator was created. Additions or removals to
the set of matching services subsequent to the creation of the
iterator are not visible during iteration.
Once an iterator has been obtained, it is possible that the service
backing one of the proxies in the set is unregistered. There is *no
locking* to protect against this. When a backing service is
unregistered in this way no attempt is made to find a replacement
service. If an operation is invoked on a proxy with no backing
service then :
- if a service-unavailable-handler has been specified then the
handler is invoked and the result from the handler is returned
to the client
- if no service-unavailable-handler has been specified, then a
ServiceUnavailableException is thrown.
* semanics of 1..n cardinality
The semantics of an osgi:reference with 1..1 cardinality are the same
as those given for 0..1, with the following exceptions:
* unless the wait-for-dependencies=false directive is specified in
the manifest, the creation of the application context will be
deferred until at least one matching service is available.
* when the last service remaining in the set is unregistered such
that the set is empty, then *no action is taken until an iterator
over the Set is requested*. If at this point in time (and after
waiting for any timeout specified) there are still no available
matching services then the behaviour is as described for an
unsatisfied 1..1 reference.
OsgiServiceTemplate
====================
Proposed addition to API, usage is entirely optional.
As an alternative to Set iteration for osgi:reference beans with x..n
cardinality, the OsgiServiceTemplate class provides an API for
invoking the backing services. It is similar in conception to the
other Spring template classes - managing iteration on behalf of
clients and using a callback mechanism for the business logic:
public class OsgiServiceTemplate {
public OsgiServiceTemplate(Set services) {...}
/**
* Iterates over all services in Set, calling invoker.doWithService
* for each member with an active backing service.
* Catches any ServiceUnavailableExceptions and passes them to
* invoker.handleUnavailableService(ServiceUnavailableException ex)
*/
public void doWithServices(ServiceInvoker invoker) {..}
}
interface ServiceInvoker {
void doWithService(Object service);
void handleUnavailableService(ServiceUnavailableException ex);
}
Java 5 versions of these types that avoid casting from Object will
also be provided.
semantics of bind and unbind callbacks
========================================
bind and unbind callbacks are invoked as part of the synchronous
handling of register and unregister events for backing services.
web applications
=================
Proposal was sent to this list earlier today...
Non-object contributions:
=========================
Consider the scenario where two 'domain model' bundles D1 and D2
(partitioning some domain) are used in an application. The domain
types are persisted using Hibernate, and each persistent type has a
corresponding .hbm.xml file located alongside it in the bundle
declaring the persistent type. Yet another bundle defines the
SessionFactoryBean. Creation of the session factory requires
visibility of the hbm.xml files. How does the factory know which
mapping files to use? The eclipse extension registry provides a
possible solution to this problem (making it possible to define a
hibernate mapping extension point). This is an area for further
investigation. Note that the extension registry bundles are fairly
small and not tied to either eclipse or equinox.
I think that covers the main points. After any discussion relating to
this has settled down, I'll update the specification to 0.8 level
incorporating this material.
--
Adrian Colyer
CTO
Interface21 Limited
http://www.interface21.com
Registered in England and Wales: No. 5187766 Registered Office: Summit
House, 2-2A Highfield Road, Dartford, Kent, DA1 2JY, UK
E-mails should be checked by the recipient to ensure that there are no
viruses and Interface21 does not accept any responsibility if this is
not done. Any views or opinions presented are solely those of the
author and do not necessarily represent those of Interface21.
----- End forwarded message -----
--
Adrian Colyer
CTO
Interface21 Limited
http://www.interface21.com
Registered in England and Wales: No. 5187766 Registered Office: Summit
House, 2-2A Highfield Road, Dartford, Kent, DA1 2JY, UK
This e-mail and any attachments transmitted with it are strictly
confidential and intended solely for the person or entity to whom they
are addressed. Unauthorised use, copying, disclosure or distribution
is prohibited. If you receive this e-mail in error please notify the
sender immediately and then delete it along with any attachments.
E-mails should be checked by the recipient to ensure that there are no
viruses and Interface21 does not accept any responsibility if this is
not done. Any views or opinions presented are solely those of the
author and do not necessarily represent those of Interface21.