Spring OSGi Specification (v0.7)
1.0 Introduction
The Spring Framework is the leading full-stack Java/JEE application framework. It provides a lightweight container and a non-invasive programming model enabled by the use of dependency injection, aop, and portable service abstractions. OSGi offers a dynamic application execution environment in which components (bundles) can be installed, updated, or removed on the fly. It also has excellent support for modularity and versioning.
The goal of Spring’s OSGi support is to make it as easy as possible to write Spring applications that can be deployed in an OSGi execution environment, and that can take advantage of the services offered by the OSGi framework. Spring’s OSGi support also makes development of OSGi applications simpler and more productive by building on the ease-of-use and power of the Spring Framework. For enterprise applications, we envisage this will offer the following benefits:
- Better separation of application logic into modules
- The ability to deploy multiple versions of a module concurrently
- The ability to dynamically discover and use services provided by other modules in the system
- The ability to dynamically deploy, update and undeploy modules in a running system
- Use of the Spring Framework to instantiate, configure, assemble, and decorate components within and across modules.
- A simple and familiar programming model for enterprise developers to exploit the features of the OSGi platform.
We believe that the combination of OSGi and Spring offers the most comprehensive model available for building enterprise applications.
It is not a goal of Spring’s OSGi support to provide a universal model for the development of any OSGi-based application, though some OSGi developers may of course find the Spring model attractive and choose to adopt it. Existing OSGi bundles and any services they may export are easily integrated into applications
using the Spring OSGi support, as are existing Spring configurations.
The Spring OSGi supported is targeted at OSGi R4 and above, and JDK level 1.3 and above.
This specification assumes some knowledge of both Spring and OSGi. See the introductory whitepapers “OSGi for Spring developers” and “Spring for OSGi developers” for background. Note: these whitepapers do not yet exist at time of writing!
2.0 Bundles and Application Contexts
The unit of deployment (and modularity) in OSGi is the bundle. A bundle known to the OSGi runtime is in one of three steady states: installed, resolved, or active. Bundles may export services that are made available for other bundles to discover and to use.
In Spring the primary unit of modularity is an application context, which contains some number of beans (objects managed by the Spring application context). Application contexts can be configured in a hierarchy such that a child application context can see beans defined in a parent, but not vice-versa. The Spring concepts of exporters and factory beans are used to export references to beans to clients outside of the application context, and to inject references to services that are defined outside of the application context.
There is a natural affinity between an OSGi bundle and a Spring application context: an active bundle may contain a Spring application context, responsible for the instantiation, configuration, assembly, and decoration of the objects (beans) within the bundle. Some of these beans may optionally be exported as OSGi services and thus made available to other bundles, beans within the bundle may also be transparently injected with references to OSGi services.
2.1 Creating an Application Context within a Bundle
An application context is configured using one or more XML configuration files defining beans. (Strictly, an application context is agnostic to configuration format, but XML is the most frequently used). The XML documents containing the configuration information are placed in the META-INF/spring folder in the bundle. By default, Spring will consider every document in the folder with the ”.xml” extension as defining part of the configuration for the application context.
This default behaviour can be overridden using the optional Spring-Context manifest header. The value of the header is a comma-separated list of resource paths and directives
Spring-Context ::= context ( ’,’ context ) *
> context ::= path ( ’;’ path ) * (’;’directive) *
Each path is treated as a path to a resource defined in the bundle, for example:
Spring-Context: config/application-context.xml,config/security.xml
Spring will automatically create an application context whenever a bundle with a Spring-Context manifest entry or resources in the META-INF/spring folder is activated. To enable this support you must first install and start the supplied org.springframework.osgi.extender bundle in your OSGi runtime.
When an application context is first created it examines the OSGi service references within the configuration to see if there are any service references that specify a non-optional cardinality (i.e. that specify either 1..1 or 1..n). Initialization of the context will not complete until all of these required services are available. The wait-for-dependencies directive can be set to false in the Spring-Context header to change this behaviour. When wait-for-dependencies is set to false, creation of an application context will fail if all the required services are not available at the time of activation.
The manifest header entry:
Spring-Context: *;wait-for-dependencies:=false
indicates that all xml files in META-INF/spring should be used for configuration, and that the context creation is to fail if required services are not immediately available.
A header entry:
Spring-Context: config/application-context.xml;wait-for-dependencies:=false
indicates that the config/application-context.xml should be used to configure the application context, and the context creation is to fail if required services are not immediately available.
The application context is automatically published as an OSGi service as an instance of org.springframework.context.ApplicationContext. In addition a service property org.springframework.context.service.name is set to the bundle symbolic name of the bundle hosting the application context. Publishing of the context as a service can be disabled by specifying the “publish-context:=false” directive in the Spring-Context manifest entry.
Note: The application context is published as a service to facilitate testing and administration. Use of the <osgi:reference> and <osgi:service> elements is the preferred way to obtain a reference to a bean defined in another application context (as opposed to invoking getBean() on the application context service). The reason is that by composing services using <osgi:reference> and <osgi:service>, the OSGi infrastructure will ensure that a bean only sees services with compatible versions of types, whereas if an application context is looked up in the registry, and then an object returned via getBean(...), then the only guarantee of type compatibility is that the ApplicationContext type itself is compatible. Clearly this guarantee would not be strong enough in a system with multiple concurrent versions of bundles deployed.
2.2 Spring’s resource abstraction
Spring loads resources within an application context using a Spring ResourceLoader. Relative resource paths are interpreted by the application context in a manner appropriate to the application context type (for example, class path based context, or a web-app based context). For OSGi application contexts, a relative resource path is interpreted as a resource to be loaded from the bundle classpath. If a resource path starts with the “bundle:” prefix then only the bundle itself and its attached fragments are searched for the given resource.
2.3 BundleContextAware
Spring encourages the development of applications based on simple objects that have no environmental assumptions or dependencies. If however a Spring bean does need access to its BundleContext for some reason, then the bean class can implement the org.springframework.osgi.context.BundleContextAware interface. Beans implementing this interface will be injected with their BundleContext when they are instantiated in the application context.
2.4 Use of the Context ClassLoader
Many useful 3rd-party libraries and applications exist which know nothing about OSGi and rely on the Thread’s context ClassLoader for the dynamic loading of classes. OSGi does not define what the context ClassLoader will be at any point in time. This fact coupled with OSGi’s non-hierarchical class loading means that such libraries will not be able to find the types and resources they need.
Consider a simple example of an enterprise application packaged in bundle A, creating a Hibernate SessionFactory using Hibernate types exported by bundle H. The SessionFactory will need to load application types and resources defined in bundle A, but these are not visible to it in an OSGi environment. The context ClassLoader for any thread that calls from bundle A into bundle H must be set to A’s bundle ClassLoader in order for this to work.
The Spring-OSGi support ensures that when activating a bundle, the context ClassLoader is always set to a ClassLoader that can see the resources of the bundle being activated. Thus calls to libraries made during bean instantiation and configuration will always be made in the context of an appropriate context ClassLoader.
It is also possible to request Spring to manage the context ClassLoader on calls made to OSGi services, and for the invocation of operations on beans exposed as OSGi services. See section 3 for more details on this support.
2.4.1 Other contexual access
A Spring bean can implement the BundleContextAware interface to be injected with a reference to the BundleContext of the bundle in which it resides. During bundle activation and whenever invoking an OSGi service accessed as a Spring bean, Spring will also provide access to the “current” bundle’s BundleContext through a ThreadLocal variable, accessible via LocalBundleContext.getContext.
2.5 Web application support
Spring uses a ServletContextListener, org.springframework.web.context.ContextLoaderListener to automatically create a WebApplicationContext for web applications. An OSGi enabled version of WebApplicationContext, org.springframework.osgi.context.support.WebApplicationContext is provided for use with web applications running inside OSGi. To use this support, set the contextClass parameter of the listener declaration in your web.xml file to “org.springframework.osgi.context.support.WebApplicationContext”.
3.0 Services and the dynamic nature of the OSGi platform
OSGi is a dynamic platform: bundles may be installed, started, updated, stopped, and uninstalled at any time during the running of the framework. In this section we explore what this means from the perspective of an application context and the services it publishes and accesses.
When an active bundle is stopped, any services it exported during its lifetime are automatically unregistered and the bundle returns to the resolved state. A stopped bundle should release any resources it has acquired and terminate any threads. Packages exported by a stopped bundle continue to be available to other bundles.
A bundle in the resolved state may be uninstalled: packages that were exported by an uninstalled bundle continue to be available to bundles that imported them (but not to newly installed bundles).
A bundle in the resolved state may also be updated. The update process migrates from one version of a bundle to another version of the same bundle.
Finally of course a resolved bundle can be started, which transitions it to the active state.
The OSGi PackageAdmin refreshPackages operation refreshes packages across the whole OSGi framework or a given subset of installed bundles. During the refresh, an application context in an affected bundle will be stopped and restarted. After a refreshPackages operation, packages exported by older versions of updated bundles, or packages exported by uninstalled bundles, are no longer available. Consult the OSGi specifications for full details.
3.1 Starting and stopping bundles defining application contexts
When a bundle containing Spring resources is activated an application context for the bundle will automatically be created (see section 2.1). When such a bundle is subsequently stopped, the application context will be closed and destroyed. Any singleton beans in the context implementing the DisposableBean interface or specifying a destroy-method in their configuration will receive notification. When the application context is closed, all OSGi services exported by the bundle are unregistered.
3.2 OSGi Services
An OSGi service is an object published in OSGi’s service registry, supporting an advertised interface. A service can also be published with a set of queryable properties.
OSGi services are dynamic in nature and therefore applications using services need to have mechanisms for dealing with this. OSGi provides several different mechanisms for dealing with dynamic services, including the Declarative Services (DS) specification.
When using DS, a service component is activated when all of its dependencies are satisfied, and may be configured with simple property values (primitive types, string values and arrays or vectors of these) and with references to published OSGi services. A component itself may be registered as an OSGi service under a given interface implemented by the component.
Service components are similar to Spring beans in that they are backed by simple Java objects and have their dependencies injected. Compared to Spring beans however the dependency injection support is very limited, and there is no support for any of the higher-order functions that Spring makes available for beans such as AOP, declarative transactions, security, management, exporting and importing to destinations other than OSGi services, and so on. It is also not possible to assemble together a set of service components within a bundle without also exporting any injected service references as full OSGi services. Spring allows full configuration and assembly of beans within a bundle, but does not require a bean to be exported outside of a bundle simply for another bean in the same bundle to use it. Thus a distinction can be made between public (exported) and private (non-exported) beans.
DS requires (quite reasonably!) that services be packaged as OSGi-specific service components and configured using OSGi-syntax. When using OSGi services
in conjunction with Spring this is potentially confusing to users, as there is overlap between DS concepts and syntax and Spring beans.
The Spring-OSGi support is able to manage dependencies between services declared
in Spring application contexts and can support the same delayed activation semantics as DS. Spring also fully supports the dynamic publishing and accessing of services. Spring-OSGi enabled bundles can co-exist happily with bundles using DS, but for new bundle development we recommend the use of the Spring OSGi support in place of DS.
The Spring approach is to provide a simple and consistent programming model both inside and outside of the OSGi environment. This facilitates testing outside of OSGi and makes it easier for enterprise Java developers not familiar with the intricacies of the OSGi programming model to become productive quickly. At the same time, if an application does need to work directly with OSGi for advanced features this is supported.
3.3 Using OSGi services
The <osgi:reference> element is used to define a local bean that acts as a proxy to an OSGi service (or set of services). The only required attributes are id (which defines the name of the local bean) and interface (which defines the fully qualified name of the interface that the target service is registered under).
For example, to define a local bean representing the MessageService service you would specify:
<osgi:reference id="messageService"
interface="com.xyz.messaging.MessageService"/>
The filter attribute can be used to specify an optional OSGi framework filter expression that further constrains the set of target services.
The optional depends-on attribute ensures that the named dependency is instantiated before the reference bean.
The optional cardinality attribute allows a reference cardinality to be specified (0..1, 1..1, 0..n, or 1..n). The default is “1..1”. For references with cardinality 0..1 or 1..1 the <osgi:reference> element resolves to a bean of the interface type specified in the interface attribute. For references with cardinality 0..n or 1..n, the <osgi:reference> element resolves to a Collection with elements of the interface type.
Consider the following fragment of configuration:
<osgi:reference id="messageService" interface="MessageService"/>
<osgi:reference id="listeners" interface="MyEventListener"
cardinality="0..n"/>
<bean id="myBean" class="SomeClass">
<property name="messageService" ref="messageService"/>
<property name="eventListeners" ref="listeners"/>
</bean>
The class “SomeClass” backing “myBean” would be defined as follows:
public class SomeClass {
private MessageService msgService;
private Collection<MyEventListener> listeners;
public void setMessageService(MessageService aService) {
this.msgService = aService;
}
public void setEventListeners(Collection<MyEventListener> listeners) {
this.listeners = listeners;
}
// ...
}
Note: the raw type Collection can also be used in place of Collection<MyEventListener>, the Spring-OSGi support does not require Java 5.
3.3.1 Binding and unbinding service references
Spring gives you a constant object reference for the bean defined by an <osgi:reference> (either a proxy to the target service for 0..1 or 1..1 cardinalities, or a Spring-managed collection for 0..n and 1..n cardinalities). The services behind this reference may come and go dynamically.
For service references held in a collection, invocation of the iterator() operation results in an instance of Iterator being returned that iterates over a constant set of references (the matching references at the time the iterator() operation was invoked). The members of the collection may change at any time, so subsequent iterations over the collection (for example) may see different members.
Invoking an operation on a service reference may fail at any time with an unchecked ServiceUnavailableException (the bundle providing the service may be stopped for example). By specifying the timeout attribute in the <osgi:reference> element Spring can be configured to wait up to a given number of milliseconds for a service to become available before failing. The default behaviour is not to fail immediately (i.e. no timeout period) if a required service is not available.
For example:
<osgi:reference id="messageService" interface="MessageService" timeout="3000"/>
For optional service references (those with cardinality 0..1 or 0..n) the oneway attribute may also be set to “true” (the default is false). An invocation of an operation with a void return type via a service reference with cardinality 0..1 will not throw a ServiceUnavailableException if a target service is unavailable at the time of invocation. Likewise an operation on a service reference obtained by iterating over the collection of an <osgi:reference> bean with cardinality 0..n will not throw a ServiceUnavailableException if the operation signature has a void return type and the target service becomes unavailable before the operation is invoked.
For stateless services, this level of support may well be sufficient. In-between operation invocations, the target service may transparently updated. If a target service is unavailable when an operation is invoked, it can be retried, and only then will a ServiceUnavailableException be thrown.
For stateful services, or simply for clients that want to be more involved in service tracking, it is possible to explicitly track the availability (or otherwise) of OSGi services backing a service reference by specifying one or more listeners using the nested listener element. The following example shows two listeners registered for a service reference:
<osgi:reference id="messageService" interface="MessageService">
<osgi:listener ref="aListenerBean"/>
<osgi:listener ref="anotherListenerBean"
bind-method="serviceAvailable"
unbind-method="serviceUnavailable"/>
</osgi:reference>
If neither of the bind-method and unbind-method attributes are specified, then the class named for the bean referenced by the listener must implement Spring’s TargetSourceLifecycleListener interface. If either of the bind-method or unbind-method attributes are specified then the value of these attributes must be the name of a method in the listener bean class. Spring will invoke the named method when a backing service is bound or unbound as appropriate.
The signature of a bind or unbind operation must be one of the following:
public void some-method-name(
String serviceBeanName,
<ServiceInterfaceType> service)
public void some-method-name(String serviceBeanName, Object service)
public void some-method-name(<ServiceInterfaceType> service)
public void some-method-name(Object service)
When the bind method is invoked, the serviceBeanName parameter (if in use) is passed the name of the <osgi:reference> defined bean that is being bound (e.g. “messageService”). The service parameter is the (unchanged) service reference that proxies the target service. The bind method will be called whenever the target service is updated, including the initial binding of the target service.
Listeners defined for an <osgi:reference> element with cardinality 0..n or 1..n will be invoked every time a service is added to or removed from the collection of tracked references.
Spring will delay the creation of an application context with non-optional service references (<osgi:reference> beans with cardinality 1..1 or 1..n) until those references are satisfied. However, Spring will not automatically destroy an application context if its non-optional service dependencies subsequently become unsatisfied. If a service is truly critical to the functioning of an application context such that the context cannot continue without it, then a listener bean for the service reference can implement ConfigurableApplicationContextAware and invoke the refresh operation on the injected application context object in its unbind method. Calling refresh will destroy all disposable beans in the application context and cause it to be refreshed once all non-optional service references are satisified again.
3.3.2 References to services obtained via service factories
OSGi services may be backed by a ServiceFactory. When a service is provided by a service factory, each bundle requesting a reference to the service gets its own unique service instance. In order to support client configuration of the service created by the factory, Spring supports property elements nested inside of an <osgi:reference>. A target service backing the service reference will be dependency injected by Spring with the given properties. This injection happens every time a target service is bound to the service reference.
Note: use of nested property elements with services that are not backed by a ServiceFactory is discouraged. Ideally we would prevent this, but OSGi does not provide sufficient runtime metadata about a service to know.
3.3.3 Context ClassLoader management
OSGi does not define what types will be visible through the context ClassLoader when a client bundle invokes an operation on a service published by another bundle. (Note: future versions of the OSGi specification may make some guarantees in this regard). For many enterprise libraries that expect to make use of the context ClassLoader to load types and resources, leaving the context ClassLoader undefined is unacceptable. By default Spring ensures that the context ClassLoader has visibility of types and resources in the service invoking bundle when an operation is invoked on a service obtained via an <osgi:reference>. When the service operation has completed the context ClassLoader is restored to whatever value it had prior to the invocation.
Management of the context ClassLoader can be controlled using the context-classloader attribute of the <osgi:reference> element. The default value is client, which specifies the behaviour described above. Setting the attribute to service-provider ensures that the ContextClassLoader has visibility of types and resources in the service providing bundle when the operation is invoked. Setting the attribute to unmanaged means that Spring will not attempt to manage the context ClassLoader at all.
3.4 Exporting Spring beans as OSGi services
Any bean defined in an application context may be exported (registered) as an OSGi service. It will be automatically unregistered when the application context is stopped.
A bean is registered as an OSGi service using the <osgi:service> element. The ref attribute names the bean to be registered, and the interface attribute defines the interface type the bean is to be registered under. For example:
<osgi:service ref="myBean" interface="com.xyz.MessageService"/>
The <osgi:service> element may optionally include one or more nested service-property elements which define properties used when registering the service.
In addition to referring to the bean to be exposed by reference, it may also be defined as an anonymous inner bean of the <osgi:service> element. For example:
<osgi:service interface="com.xyz.MessageService">
<bean class="MessageServiceProvider">
<!-- ... -->
</bean>
</osgi:service>
If the lazy-init attribute is set to true then the service will not be created until it is referenced.
If the bean exposed as a service implements the OSGi ServiceFactory interface then OSGi will invoke the operations defined on that interface to ensure that each requesting bundle gets its own unique service instance.
As an alternative to implementing the OSGi ServiceFactory interface, the bean may implement Spring’s FactoryBean interface. If the singleton property of the factory bean is set to true, then all clients of the service will see the same service instance, but the service itself will not be created until it is referenced. If the singleton property is set to false then the getObject operation of the factory bean will be invoked once for each unique requesting bundle. If the service object returned by the getObject operation of a non-singleton factory implements DisposableBean then the destroy method of the returned service object will be invoked when all references to it from its associated (client) bundle have been released.
3.4.1 Context ClassLoader management
OSGi does not define what types will be visible through the context ClassLoader when a service operation executes. (Note: future versions of the OSGi specification may make some guarantees in this regard). Spring can manage the context ClassLoader for the execution of operations on <osgi:service> defined beans. The <osgi:service> element supports a context-classloader attribute that by default is set to unmanaged (Spring does not perform any additional context ClassLoader management for the execution of a service). Setting this attribute to service-provider causes Spring to ensure that the types and resources of the service publishing bundle are available via the context ClassLoader when the service executes. When the service execution has completed, the context ClassLoader is restored to whatever value it had prior to the execution.
Note: setting context-class-loader=”service-provider” will override any context ClassLoader that may have been set on an invocation of that service via an <osgi:reference> defined bean.
4.0 The OSGi Configuration Admin service
Spring provides support for bean property values to be externalized from Spring configuration files and retrieved from an alternate source. For example, the Spring PropertyPlaceholderConfigurer class can be used to replace escaped property values with values loaded from a properties file.
Spring OSGi provides additional support for sourcing bean property values from the OSGi Configuration Admin Service. To enable this support, define one or more propertyPlaceholder elements inside the Spring configuration (if using more than one, each must be configured with a unique delimiter string). For example:
<osgi:property-placeholder persistent-id="com.xyz.myapp"/>
Where “persistent-id” is the OSGi PID used as the key for the configuration data. The default delimiter is ”${...}” so for example a property with value
”${timeout}”
would be replaced with the value of the “timeout” property held by the configuration admin service under the supplied PID. If the “update” attribute is set to true then singleton beans will have new property values re-injected if changes are made to configuration settings via the admin service. Re-injection is only supported for method-based injection (not constructor).
The optional default-properties attribute can be set to the name of a bean (of type java.util.Properties or java.util.Map) that provides default property values to be used if the configuration admin service does not have a matching definition for a referenced property.
See the Spring osgi schema reference for full details of the property-placeholder element.
4.1 Atomic updates
A bean with several properties set via the configuration admin service and using the “update” facility will see multiple property updates (one for each changed property) when a set of related changes are made to the configuration.
The <osgi:config> element defines a bean of type Map containing all of the properties registered under the given persistent-id.
<osgi:config id="myAppProperties" persistent-id="com.xyz.myapp"/>
It supports zero or more nested config-listener elements that are used to define listener beans to be notified when the contents of the Map changes. The update-method attribute of config-listener is mandatory. The value of this attribute is the name of the method in the referenced listener bean to be invoked when the configuration changes. For example:
<osgi:config id="myAppProperties" persistent-id="com.xyz.myapp">
<osgi:config-listener ref="aListenerBean"
update-method="setApplicationProperties"/>
</osgi:config>
The method named in the update-method attribute must have a signature with one of the following forms:
public void update_method_name(java.util.Map properties)
or
public void update_method_name(String pid, java.util.Map properties)
The second form is intended for use with managed service factories (see section 104.6 of the OSGi services compendium). If the persistent-id specified for an <osgi:config> element is actually a factory PID for configuration information managed by an ManagedServiceFactory then the update-method will be invoked once for each set of properties registered under the factory PID.
5.0 Provisioning
OSGi does not automatically provision (install and start) bundles providing services needed by another bundle. Spring provides basic support for creating a provisioning bundle. More sophisticated provisioning function (perhaps in conjunction with Felix’s OBR) may be supported in future releases.
The osgi:bundle and osgi:virtual-bundle configuration elements define beans that represent OSGi bundles. A basic provisioning service can therefore be implemented by:
- installing and starting the OSGi Spring extender bundle
- installing and starting a “Spring” bundle with an application context configuration file that declares <osgi:bundle> and <osgi:virtual-bundle> bean declarations
From this point, Spring will manage the bring-up of the rest of the needed OSGi bundles.
5.1 The osgi:bundle element
The <osgi:bundle> element can be used to define a bean to the application context that represents another osgi bundle (the resulting bean is of type org.osgi.framework.Bundle). In its simplest form it can be used as follows:
<osgi:bundle symbolic-name="com.xyz.myapp.service"/>
If the location property is also set then the bundle will be installed if it is not already. If the “start” property is set to true, then the bundle will also be started if it is not already started.
<osgi:bundle symbolic-name="com.xyz.myapp.service"
location="http://.....some-url"
start="true"/>
5.2 Virtual bundles
The <osgi:virtual-bundle> supports the creation of bundles on the fly from jar-based artifacts. The virtual bundle mechanism allows users to manipulate all of the standard headers interpreted by OSGi. For example:
<osgi:virtual-bundle id="bundleA" depends-on="bundleB"
location="file:bundleA.jar" state="start"
version="1.0" group-id="com.me.bundles" artifact-id="bundleA">
<exports
<list>
<value>com.me.bundles.bundleA</value>
</list>
</exports>
<imports>
<list>
<value>com.me.bundles.bundleB</value>
</list>
</imports>
</osgi:virtual-bundle>
The location attribute is also allowed to be a maven pom.xml file, in
which case the appropriate versioning information will be taken from
the pom and the appropriate jar accessed using information in the pom.
5.3 Future Provisioning Support
Providing an explicit list of bundles to be installed and started assumes knowledge of which bundles export which packages and provide which services. In the future provisioning support may be developed that examines the packages needed (imported) by a bundle, and the services referenced by a bundle, and automatically installs and starts bundles that meet those requirements. Such support is out of scope for the current release.
6.0 Integration testing
The Spring framework has always promoted test-driven development and made it easy to write good unit and integration tests. Unit testing of OSGi applications presents no special requirements – simply test your application classes in isolation. It is unlikely that an application class has any dependency on OSGi. If an application class is, for example, BundleContextAware, then the OSGi Bundle and BundleContext interfaces are easy to mock up.
For integration testing (execution of tests within a running OSGi environment) some assistance is required. A recommended best practice is to develop the integration test suite for a bundle in a separate test bundle. Given a bundle “com.xyz.myapp.service” you might choose to place the integration tests in a bundle “com.xyz.myapp.service.tests” for example. Spring will provide an AbstractOsgiTests base class that makes it easy to start up an OSGi environment, and install and start the necessary bundles needed to run a test. It ensures that each test run occurs in a clean OSGi environment, and allows tests to be easily run against any supported OSGi provider (we’ll look at equinox, knopflerfish, and Felix). The OsgiAdapter interface will enable support for additional OSGi providers not supported out-of-the-box to be added.
_Update: Knopplerfish provides an integration harness for OSGi that seems to meet many of the requirements, and is not tied to the Knopplerfish implementation. This could be adopted as the integration testing vehicle. See Knopplerfish testing support
Note that the Eclipse IDE has excellent support for running bundle-based tests, but this is tied to equinox and to the Eclipse IDE, whereas Spring needs to support multiple OSGi providers, multiple IDEs, and continuous integration builds via maven and ant.
7.0 Developing web applications with Spring and OSGi
Martin Lippert and Gerd Wutherich already have the sandbox Spring OSGi code running inside a web application using the servlet container embeddable equinox support created by the server-side equinox incubator project. The Spring OSGi project targets enterprise applications, of which web applications form a large part. We will therefore be making sure that Spring web applications can be easily written and deployed using OSGi as the underlying infrastructure. This support will be based initially on the equinox incubator, and then broadened to support other OSGi providers if possible.
A sample application will be developed to show the support working in the context of a realistic web application.
8.0 Management of OSGi applications
For enterprise applications it is desirable to be able to manage an OSGi enviroment via JMX (list bundles, install/uninstall/start/stop/update etc.). If this support does not prexist (I haven’t searched yet) then Spring will provide an OSGi bundle that enables JMX-based management of OSGi using the Spring JMX support.
9.0 Packaging Spring as OSGi bundles
To support the development of Spring applications based on OSGi, it is necessary to ship Spring itself as a set of OSGi bundles (jar files). This means ensuring that all the necessary manifest entries are present in the Spring jars. This work will be done in the Spring 2.1 timeframe. Each of the jars shipped in “dist/modules” of the Spring distribution will be shipped as a valid OSGi bundle (the OSGi manifest entries are harmless when using the jars outside of an OSGi environment). A new listener jar will also be provided with functionality already described.
All resource loading and class loading should, wherever possible, be done using the bundle classloader, and not via the context classloader. When an application context is created inside an OSGi bundle the bundle classloader is set as the application context classloader, and the the ResourceLoader interface supported by the ApplicationContext is also implemented on top of the OSGi services. As of Spring 2.0-rc2 this is sufficient to load all Spring dependent classes and resources using this mechanism.
10.0 Support for deploying Spring applications into an OSGi environment
TODO: describe maven plugin / ant task / SpringIDE extensions to allow easy packaging of a Spring application as an OSGi bundle or set of bundles.
Appendix A. Deploying existing application code to OSGi
It is likely that existing application code (especially frameworks and libraries) will be deployed in an OSGi environment after only minimal intervention to turn jar files into valid OSGi bundles (there are ant and maven tasks to assist in this process for example). Because the classloading and isolation properties of OSGi are different than that typically encountered in enterprise applications, this raises a number of issues to be aware of.
- Resources in META-INF cannot be directly accessed across bundles through the classloader. Spring’s resource loading abstraction handles this issue for Spring-based resource loading by the following algorithm:
> 1. Find the owning bundles of all exported packages that are imported by the requesting Bundle (this is done via the PackageAdmin service)
> 2. Delegate the request to Bundle.getEntry for each contributing bundle
> 3. Aggregate and return the results
- A framework or library cannot rely on the context class loader to gain visibility of application types. This results in ClassNotFoundExceptions if a library tries to load an application type via reflection. In equinox the ContextFinder mechanism provides a work-around for this issue. On other platforms this mechanism is not yet supported. (Equinox also supports a “buddy” mechanism that provides another solution to this issue). We will investigate the use of Spring AOP to set an appropriate context class loader around the execution of bean operations for use when the underlying OSGi platform does not provide a native solution. Such a classloader would also support the “resources in META-INF” resolution strategy outlined above.
Appendix B. spring-osgi.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/osgi"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.springframework.org/schema/osgi"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<!-- reference -->
<xsd:element name="reference" type="Treference"/>
<xsd:complexType name="Treference">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element name="property" type="beans:propertyType" minOccurs="0"/>
<xsd:element name="listener" type="Tlistener" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="interface" use="required" type="xsd:string"/>
<xsd:attribute name="filter" use="optional" type="xsd:string"/>
<xsd:attribute name="cardinality" use="optional" type="Tcardinality" default="1..1"/>
<xsd:attribute name="timeout" use="optional" type="xsd:int" default="0"/>
<xsd:attribute name="depends-on" type="xsd:string" use="optional"/>
<xsd:attribute name="context-classloader"
type="TreferenceClassLoaderOptions" default="client"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="Tcardinality">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="0..1"/>
<xsd:enumeration value="0..n"/>
<xsd:enumeration value="1..1"/>
<xsd:enumeration value="1..n"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="TreferenceClassLoaderOptions">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="client"/>
<xsd:enumeration value="service-provider"/>
<xsd:enumeration value="unmanaged"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="Tlistener">
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="bind-method" type="xsd:string" use="optional"/>
<xsd:attribute name="unbind-method" type="xsd:string" use="optional"/>
</xsd:complexType>
<!-- service -->
<xsd:element name="service" type="Tservice"/>
<xsd:complexType name="Tservice">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element name="interfaces" type="beans:listOrSetType" minOccurs="0"/>
<xsd:element name="service-properties" type="beans:propsType" minOccurs="0"/>
<!-- this next entry is to allow a nested bean element -->
<xsd:any namespace="##other" minOccurs="0" maxOccurs="1" processContents="skip"/>
</xsd:sequence>
<xsd:attribute name="ref" type="xsd:string" use="optional"/>
<xsd:attribute name="interface" type="xsd:string" use="optional"/>
<xsd:attribute name="lazy-init" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="depends-on" type="xsd:string" use="optional"/>
<xsd:attribute name="context-classloader"
type="TserviceClassLoaderOptions" default="unmanaged"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="TserviceClassLoaderOptions">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="service-provider"/>
<xsd:enumeration value="unmanaged"/>
</xsd:restriction>
</xsd:simpleType>
<!-- property placeholder -->
<xsd:element name="property-placeholder" type="TpropertyPlaceholder"/>
<xsd:complexType name="TpropertyPlaceholder">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="persistent-id" type="xsd:string" use="required"/>
<xsd:attribute name="placeholder-prefix" type="xsd:string" use="optional" default="${"/>
<xsd:attribute name="placeholder-suffix" type="xsd:string" use="optional" default="}"/>
<xsd:attribute name="default-properties" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!-- config object -->
<xsd:element name="config" type="Tconfig"/>
<xsd:complexType name="Tconfig">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element name="config-listener" type="TconfigListener"/>
</xsd:sequence>
<xsd:attribute name="persistent-id" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="TconfigListener">
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="update-method" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- bundle -->
<xsd:element name="bundle" type="Tbundle"/>
<xsd:complexType name="Tbundle">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="symbolic-name" type="xsd:string" use="optional"/>
<xsd:attribute name="depends-on" type="xsd:string" use="optional"/>
<xsd:attribute name="location" type="xsd:string" use="optional"/>
<xsd:attribute name="state" type="Tstate" use="optional"/>
<xsd:attribute name="start-level" type="xsd:int" use="optional" default="0"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="Tstate">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="start"/>
<xsd:enumeration value="stop"/>
<xsd:enumeration value="install"/>
<xsd:enumeration value="uninstall"/>
<xsd:enumeration value="update"/>
</xsd:restriction>
</xsd:simpleType>
<!-- virtual-bundle -->
<xsd:element name="virtual-bundle" type="TvirtualBundle"/>
<xsd:complexType name="TvirtualBundle">
<xsd:complexContent>
<xsd:extension base="Tbundle">
<xsd:all>
<xsd:element name="imports" type="TpackageList" minOccurs="0"/>
<xsd:element name="exports" type="TpackageList" minOccurs="0"/>
<xsd:element name="dynamic-imports" type="TpackageList" minOccurs="0"/>
</xsd:all>
<xsd:attribute name="version" type="xsd:string" use="optional"/>
<xsd:attribute name="group-id" type="xsd:string" use="optional"/>
<xsd:attribute name="artifact-id" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="TpackageList">
<xsd:sequence>
<xsd:element name="package" type="Tpackage" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Tpackage">
<xsd:sequence>
<xsd:element name="uses" type="TpackageList" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="version" type="xsd:string" use="optional"/>
</xsd:complexType>
</xsd:schema>
Appendix C. Acknowledgements
Thanks to Peter Kriens, Jeff McAffer, Richard S. Hall, Andy Piper, Hal Hildebrand, Glyn Normington, Martin Lippert, Gerd Wuetherich, Bill Gallagher, BJ Hargrave, and Olivier Gruber for their contributions in the development of this specification.








