Spring and OSGiVersion 0.6 1.0 IntroductionThe 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. The converse is also true – Spring/OSGi will make it as easy as possible to write OSGi applications by leveraging the ease-of-use and power of Spring. For enterprise applications, we envisage this will offer the following benefits:
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 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 ContextsThe 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 OSGi ServicesOSGi 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-synytax. When using OSGi services The Spring OSGi support is able to manage dependencies between services declared Whether using Spring’s service dependency support or declarative services, Spring fully supports the dynamic publishing and accessing of services. 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. It is entirely possible to mix both OSGi service components and Spring beans in the same OSGi runtime. 2.2 Creating an Application Context within a BundleAn application context is configured using one or more XML configuration files defining beans. (An application context is actually agnostic to configuration format, but XML is the most frequently used). The XML documents containing the configuration information are specified using the Spring-Context header in the bundle manifest. The value of the header is a comma-separated list of resource paths.
Note: this follows the same format as the Service-Component header which is used to define the location of service component configuration files for declarative services. If no Spring-Context header is specified, then Spring will look by default for a configuration file named META-INF/bundle symbolic name-context.xml. Spring supports a number of options for creating an application context when a bundle is activated. These are the ContextLoaderBundleListener, the ContextLoaderBundleActivator, and the ContextLoaderServiceComponent. 2.2.1 ContextLoaderBundleListenerThe simplest option is to deploy the supplied org.springframework.osgi.listener bundle into your OSGi runtime. When this bundle is started, A bundle is considered to “contain Spring resources” if it defines the manifest header “Spring-Context”, contains a resource “META-INF/bundle symbolic name-context.xml”, or contains a resource “META-INF/bundle name-context.xml”. Spring resources are searched for in this order, if a match is found no other location is consulted. The newly created application context is also automatically published as an OSGi service (enabling another bundle to access this context and hence use the ApplicationContext interface to obtain references to beans by name). The application context is published as an instance of
Finally, if the bundle manifest also contains the Spring-Parent-Context header then the value of this header is interpreted as the value of the org.springframework.context.service.name property used to look up the parent application context service (usually in another bundle) for this application context.
When specifying a parent context in this way, the beans in the application context will not be instantiated until the parent context service is published and available. If fail-fast behaviour is required then this can be configured through the header:
This header also controls application context instantiation based on the availability of services that it depends on. When the context is first created it searches its bean definitions for <osgi:reference> beans. If it finds any it registers itself as a listener for service activation and will not complete initialization until all the services are available. The status of the context can be determined through the method The listener bundle is also able to read a global application context configuration file. This can be used to install and activate bundles and virtual bundles (see section 2.7.1 for details) under the control of Spring. Beans defined in the global application context have visibility of the types exported by bundles installed in this way. By default the global configuration file is named spring.xml and is searched for using standard Spring/OSGi resource loading rules. The default name can be changed, either by setting the Spring-Context header of the listener bundle itself, or by specifying an alternative location via the system property org.springframework.osgi.config.
Three application contexts, A, B and C would be created with A the parent of B and B the parent of C. In this way it is possible to load bundles in one configuration file and service wiring, aspects and other global configuration in a different one. This is generally desirable when using bundle beans, since classes in bundles will not be available until the bundle beans are instantiated – and it is a tricky business having bundles and beans created in the right order when using just a single configuration file. Note: The listener support requires a way to obtain a BundleContext object given a Bundle. There is no standard way to do this, but Spring can inspect the OSGi properties to determine the runtime platform and delegate to an appropriate strategy implementation accordingly. 2.2.2 ContextLoaderBundleActivatorAn alternative to using the listener bundle is to specify the ContextLoaderBundleActivator as the bundle activator class:
This gives control over application context creation on an individual bundle basis. The creation of the context follows the same rules as defined in section 2.2.1. 2.2.3 ContextLoaderServiceComponentFor advanced scenarios you can exploit DS directly and have DS create the application context for you.To do this you must use a service component to initialize the application context. A component XML file must be created that defines a component with implementation class org.springframework.osgi.context.ContextLoaderServiceComponent. The definition of the component will look as follows: <component name="my.application.context"> <implementation class="org.springframework.osgi.context.ContextLoaderServiceComponent"/> <reference name="parentContext" interface="org.springframework.context.ApplicationContext" target="(org.springframework.context.service.name=_parent-context-name_)" /> <!-- additional references can be defined here if desired, this will prevent activation of the application context until all references are satisfied --> </component> The component file must be named in a Service-Component header defined in the bundle’s manifest (this is part of the Declarative services specfication, not simply a Spring requirement):
Using the scr namespace, it is possible to share a single configuration file used by both Spring and the service component runtime :- name the same configuration file in both the Service-Component and Spring-Context headers, and simply include the component definition shown above inside the Spring configuration file. For example: <beans xmlns="http://www./schema/beans" xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:osgi="http://www./schema/osgi" xmlns:scr="http://www./nmlsn/scr/v1.0.0" xsi:schemaLocation="http://www./beans http://www./schema/beans/spring-beans.xsd http://www./schema/osgi http://www./schema/osgi/spring-osgi.xsd"> <!-- OSGi component definition --> <scr:component name="my.application.context"> <scr:implementation class="org.springframework.osgi.context.ContextLoaderServiceComponent"/> <scr:reference name="parentContext" interface="org.springframework.context.ApplicationContext" target="(org.springframework.context.service.name=_parent-context-name_)" /> <!-- additional references can be defined here if desired, this will prevent activation of the application context until all references are satisfied --> </scr:component> <!-- Spring bean definition --> <bean id="myBean" class="a.b.c.D"/> </beans> AMC: TODO - verify that I can make this embedding work! 2.2.4 Choosing an activation mechanismWe recommend the use of the ContextLoaderBundleListener for most applications. If you have bundles containing Spring resources that you want to install and activate without creating a corresponding application context then you need to use the ContextLoaderBundleActivator instead. This must be specified as the activator of each individual bundle that should create an application context. It is not recommended to use both the ContextLoaderBundleListener and the ContextLoaderBundleActivator in the same OSGi runtime. Spring supports delayed activation of a context based on the availability of a parent context and/or other OSGi services required by beans in the context. If your application also requires an application context to be automatically destroyed when it’s dependencies cease to be satisfied (and started up again if the dependencies become satisfied once more) then you need to use the ContextLoaderServiceComponent approach. See section 3.0 “The dynamic nature of the OSGi platform” for more details. 2.3 Spring’s resource abstractionSpring 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.4 The OSGi Configuration Admin serviceSpring 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 supports the sourcing of 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
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. See the Spring osgi schema reference for full details of the property-placeholder element. 2.5 BundleContextAwareSpring 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.6 Creating Application Contexts that span multiple bundlesSo far we have only considered the case where all the resources for an application context are contained within a single bundle. It is entirely possible to create a single application context using resources that span multiple bundles. The application context owning bundle (the one that actually creates the application context) would use one of the techniques already described (bundle activator, bundle listener or SCR component) to create the application context. This bundle must have appropriate import statements in its manifest to import packages from other bundles into its namespace. This could be for example Import-Package, DynamicImport-Package or Require-Bundle manifest headers. Both Import-Package and Require-Bundle headers are resolved as part of the bundle resolution process, meaning that the application context owning bundle cannot be resolved (and hence started) until the bundles it depends on have also been resolved. If you are using the DynamicImport-Package mechanism, or depending on services started by other bundles without naming them as references in an application context SCR component definition, then it is possible for the application context owning bundle to be resolved and started before bundles that it depends on are resolved (and started). The <osgi:bundle> element can be used to define a bean to the application context that represents another osgi bundle (the 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"/> Beans in the application context that depend on services provided by such bundles can ensure that the service-providing bundle is started before any service references are resolved by “depending on” the bundle bean. For example: <osgi:bundle symbolic-name=com.xyz.myapp.service" location="http://.....some-url" start="true"/> <bean id="myBean" class="com.xyz.SomeClass" depends-on="com.xyz.myapp.service lazy-init="true"> ... </bean> Note the use of lazy-init which is needed to prevent Spring from eagerly loading the bean class before the bundle has been installed. Note: Using this facility can be convenient for integration testing and for enterprise applications with relatively static configurations, but hardwiring dependencies on certain bundles also has drawbacks as you lose the ability for the OSGi resolution process to meet the dependencies of your bundle in the best way possible, regardless of the bundles that actually provide the needed services and packages. It is best to remain ignorant of bundles if you possibly can. The OSGi Start Level Service offers a better way to manage start order and dependencies in many situations. 2.6.1 Virtual bundlesIn addition to <osgi:bundle> which allows representation and manipulation of existing bundles users may also use <osgi:virtual-bundle> to create bundles on the fly from jar-based artifacts. Currently OSGi does not support a facility for manipulating bundle headers programmatically, but the support can be provided on any OSGi platform by modifying the jar as it is loaded. 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 2.7 Use of the ContextClassLoader and other contextIn an ideal OSGi world, all classes need dynamically would be loaded by the appropriate BundleContext with no ambiguity about the context in which they are loaded. Unfortunately many useful 3rd-party libraries and applications exist which know nothing about OSGi and instead rely on the Thread’s ContextClassLoader for the dynamic loading of classes. While this may be appropriate for a J2EE environment where the ContextClassLoader can be ensured to be appropriate it is difficult to achieve in an OSGi environment where “context” does not have that much meaning. However, in a Spring managed environment it is possible to do a reasonable job, albeit with some corner cases not catered for. The two most common and supportable scenarios are:
In these two scenarios it is possible to configure Spring/OSGi to set the ContextClassLoader to something appropriate for delegating to the target bundle. However, this support is strictly optional and should be used with care. For bundle activation individual bundles can set the header
to enable ContextClassLoader support while the application context is created. A similar effect can be achieved from within the entire application context by instantiating the magic bean:
although this will not be able to set the ContextClassLoader until part way through the initialization of the application context. For bundles created via <osgi:bundle> or <osgi:virtual-bundle> in the listener bundle, simply set the push-bundle-as-context-classloader property on the bundle declaration. Likewise for service calls, again set the push-bundle-as-context-classloader property on the appropriate <osgi:reference> reference. The ContextClassLoader will then be pushed for the duration of the service call to a classloader that utilizes the target bundle’s BundleContext. 2.7.1 Other contexual accessAlthough the ContextClassLoader can be used to provide contextual information about the “current” bundle, it is not really suitable for this use since applications and bundles may also manipulate the ContextClassLoader. Additionally it does not provide enough access to OSGi services from code embedded within OSGi-unaware code (and where Spring’s BundleContextAware feature is not supportable). For this reason Spring/OSGI provides a context-sensitive access to the “current” bundle’s BundleContext. Note that the same reservations and limitations for ContextClassLoader support apply to this support. Access to the BundleContext is supplied via 2.7.2 Context-aware propertiesLibraries that rely on System properties for configuration (for example, log4j) need to see their own “version” of system properties – a single global set of properties will not suffice. To solve this Spring/OSGi provides another magic bean that replaces the Properties object backing the System properties with one that provides a level of indirection based on the current LocalBundleContext. To enable this feature instantiate the magic bean: <osgi:context-aware-properties> <props> <prop key="...">...</prop> </props> </osgi:context-aware-properties> Where the bean is pre-populated with the provided properties merged with the original System properties. 2.8 Web application supportSpring provides support for web applications to create application contexts using ContextLoaderListener. Spring/OSGi extends this support to allow Spring-enabled web applications running inside a container that is itself an OSGi bundle to access OSGi services using all the power of Spring/OSGi. This support is provided by a custom WebApplicationContext implementation The custom context can be configured by specifying the context parameter WebApplicationContext’s have their own rules regarding resource loading and the BundleAware version extends these rules to allow resources to be loaded from the current bundle. 3.0 The dynamic nature of the OSGi platformOSGi 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. 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 contextsWhen using the ContextLoaderBundleActivator, an application context is started when the containing bundle is started. If a parent context is specified, then this parent context must already be started (the OSGi Start Level Service could be used to ensure this, for example). If an application context is defined as an SCR component, then the application context is not started until the containing bundle is started, and the references of the context (including the parent context reference) are satisfied. If the application context has been defined as an immediate component (the default) then the context will be started as soon as its dependencies are satisfied. If it has been defined as a delayed component then it will registered in the service registry, but not actually activated until there is a request for it. This technique can result in a reduction in initialization time and a delay in memory footprint. Stopping a bundle that contains an application context stops the application context. If the bundle defining a parent application context is stopped, then the parent application context service will be automatically unregistered. Any child application contexts are also stopped automatically and unregistered. The behaviour when a previously stopped parent context is restarted (the bundle containing it is restarted) depends on the mechanism used to start any child contexts. If a child context was started as an SCR component then as soon as its references become satisfied again (the parent context service is published to the service registry) the child context will be restarted automatically (a new application context instance is created). If a child context was started using the bundle activator, then it is not automatically restarted, and you must stop (optionally update) and then restart the child context containing bundle explicitly. For this reason defining an application context as an SCR component is the generally prefered approach on an OSGi R4 platform (the Declarative Services support is not available on OSGi R3). An application context defined as an SCR component is automatically stopped and restarted whenever any of its references becomes unsatisfied (and subsequently satisfied again). A reference is satisfied when it specifies optional cardinality or there is at least one target service for the reference. Be aware of this behaviour when specifying a set of references for an application context component. 3.2 Injecting references to beans defined in other application contextsA bean defined in an application context can “see” any bean defined in the same context or in a parent context. In an OSGi environment, multiple application contexts may be simultaneously active and it is also possible to inject references to beans defined in other application contexts (even when those contexts are not parent contexts). The <osgi:bean> element is used for this purpose, it defines a local proxy to a bean defined by another application context. For example: <osgi:bean id="someBeanName" application-context="context-name"/> where context-name is the name that the application context defining the bean was published with (this defaults to the bundle symbolic name of the context defining bundle). Used as shown, this defines a local bean someBeanName which is a proxy to the bean named someBeanBean in the target application context. To use a different name for the local and target beans, specify the target name using the target-bean attribute. Spring automatically tracks the application context hosting the target bean (the target application context). The target application context can be stopped and subsequently restarted. When an operation is invoked on the local proxy, it will always be dispatched to the current instantiation of the target bean. If an operation is invoked during a window in which the target application context is stopped, then the operation will be transparently retried in the hope that the target context is restarted. The number of retries and the delay between retries is configurable. If the service remains unavailable at the end of this period, an OsgiServiceUnavailable exception is thrown. A bean referenced by an <osgi:bean> element is not looked up until the first operation is invoked on it, thus is it is not necessarily required that a target application context be started before the context defining the referring bean. Specifying a non-optional reference to the target application context service in an SCR component application context definition will ensure that the referring context is not started until the target context is available should this behaviour be desired. Note that doing this will also cause the referring context to be stopped (and subsequently restarted) if the target application context service is unregistered (and subsequently re-registered). An alternative to the <osgi:bean> element is to publish a bean itself as an OSGi service, and then to simply inject a reference to the OSGi service. Injecting references to OSGi services is discussed in the next sub-section, and publishing beans as OSGi services in the one following that. 3.3 Using OSGi servicesThe <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 target attribute can be used to specify an optional OSGi framework filter expression that further constrains the set of target services. 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”. The optional broadcast attribute specifies the behaviour when an operation is invoked on a reference with multiple cardinality. If set to “true”, then the operation is invoked on all matching target services. The default is “false”, which means that any one of the matching target services will be selected. The optional push-bundle-as-context-classloader attribute pushes an appropriate ContextClassLoader implementation whenever a service function is invoked. The optional depends-on attribute ensures that the named dependency is instantiated before the reference bean. The optional listeners attribute is a comma-separated list of beans that are interested in the lifecycle of the service. Any listener is expected to implement public interface ServiceActivationLifecycleListener extends ServiceLifecycleListener { public void activate(Class serviceType); } public interface ServiceDeactivationLifecycleListener extends ServiceLifecycleListener { public void deactivate(Class serviceType); } public interface ServiceRebindLifecycleListener extends ServiceLifecycleListener { public void rebind(Class serviceType); } The appropriate function will be invoked during the target service’s lifecycle. One or more nested property elements may optionally be set on the reference. These take the same form as property elements applied to any spring bean and will be applied to the bean returned by the service. Note that this support only really makes sense in the instance that the service is backed by a ServiceFactory or non-singleton FactoryBean otherwise different callers could collide in setting property values. The result-summarizer attribute is required for any reference cardinality other than 1..1. It specifies the name of a result summarizing bean. The purpose of the result-summarizer is explained below. A service referenced by an <osgi:reference> element is not looked up until the first operation is invoked on it. When an operation is invoked on a local proxy to an OSGi service the behaviour is as follows:
A result summarizer must implement the ResultSummarizer interface: interface ResultSummarizer { public Object computeResult(String operationName, Object[] results); } Note: we could get a nicer signature using generics, but we don’t want to tie Spring provides a configurable result summarizer implementation out of the box. When defining an application context as an SCR component, you can optionally define services referenced by beans within the context as references in the component definition. The context will not be started until any references specified here are satisfied, and will be automatically stopped (and subsequently restarted) if a reference becomes unsatisfied (and subsequently satisfied again). Only express references with a non-optional cardinality here if their satisfaction is critical to the whole operation of the beans in the application context and you cannot meaningfully continue without them. 3.4 Exporting Spring beans as OSGi servicesAny 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. To support the creation of components on demand (instead of all clients sharing a reference to the same instance) simply expose a bean that provides a factory interface. For example: <osgi:service ref="myMessageServiceFactory" interface="com.xyz.MessageServiceFactory"/> where the MessageServiceFactory supports a getMessageService operation which takes all the necessary parameters needed to instantiate and return a new MessageFactory. 3.4.1 Service activation on demandIf the bean referenced by <osgi:service> implements Spring’s When all <osgi:reference>’s have been destroyed it is possible for the bean to get a lifecycle callback to that effect, either by implementing the 4.0 Integration testingThe 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. 5.0 Developing web applications with Spring and OSGiMartin 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. 6.0 Management of OSGi applicationsFor 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. 7.0 Packaging Spring as OSGi bundlesTo 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. Appendix A. Deploying existing application code to OSGiIt 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.
Appendix B. spring-osgi.xsd<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://www./schema/osgi" xmlns:xsd="http://www./2001/XMLSchema" xmlns:beans="http://www./schema/beans" targetNamespace="http://www./schema/osgi" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:import namespace="http://www./schema/beans"/> <!-- bean --> <xsd:element name="bean" type="Tbean"/> <xsd:complexType name="Tbean"> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="application-context" use="required" type="xsd:string"/> <xsd:attribute name="retry-times" use="optional" type="xsd:int" default="2"/> <xsd:attribute name="retry-delay-ms" use="optional" type="xsd:int" default="1000"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> <!-- reference --> <xsd:element name="reference" type="Treference"/> <xsd:complexType name="Treference"> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:sequence> <xsd:element name="property" type="beans:propertyType" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="interface" use="required" type="xsd:string"/> <xsd:attribute name="bean-name" use="optional" type="xsd:string"/> <xsd:attribute name="target" use="optional" type="xsd:string"/> <xsd:attribute name="cardinality" use="optional" type="Tcardinality" default="1..1"/> <xsd:attribute name="broadcast" use="optional" type="xsd:boolean" default="false"/> <xsd:attribute name="result-summarizer" use="optional" type="xsd:string"/> <xsd:attribute name="retry-times" use="optional" type="xsd:int" default="2"/> <xsd:attribute name="retry-delay-ms" use="optional" type="xsd:int" default="1000"/> <xsd:attribute name="depends-on" type="xsd:string" use="optional"/> <xsd:attribute name="push-bundle-as-context-classloader" type="xsd:boolean" default="false"/> <xsd:attribute name="listeners" type="xsd:string" use="optional"/> </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> <!-- service --> <xsd:element name="service" type="Tservice"/> <xsd:complexType name="Tservice"> <xsd:all> <xsd:element name="service-properties" type="beans:propsType" minOccurs="0"/> <xsd:element name="interfaces" type="beans:listOrSetType" minOccurs="0"/> </xsd:all> <xsd:attribute name="ref" type="xsd:string" use="required"/> <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="activation-method" type="xsd:string" use="optional"/> <xsd:attribute name="deactivation-method" type="xsd:string" use="optional"/> </xsd:complexType> <xsd:complexType name="TserviceProperty"> <xsd:attribute name="name" type="xsd:string" use="required"/> <xsd:attribute name="value" type="xsd:string" use="required"/> </xsd:complexType> <!-- property placeholder --> <xsd:element name="property-placeholder" type="TpropertyPlaceholder"/> <xsd:complexType name="TpropertyPlaceholder"> <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: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:attribute name="push-bundle-as-context-classloader" type="xsd:boolean" default="false"/> </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> <!-- options --> <xsd:element name="config" type="Tconfig"/> <xsd:complexType name="Tconfig"> <!-- context properties --> <xsd:sequence> <xsd:element name="context-aware-properties" type="TcontextAwareProperties"/> </xsd:sequence> <xsd:attribute name="push-bundle-as-context-classloader" type="xsd:boolean" default="false"/> <xsd:attribute name="honor-service-dependencies" type="xsd:boolean" default="true"/> </xsd:complexType> <xsd:complexType name="TcontextAwareProperties"> <xsd:sequence> <xsd:element name="props" type="beans:propsType" minOccurs="0"/> </xsd:sequence> </xsd:complexType> </xsd:schema> Appendix C. AcknowledgementsThanks to Peter Kriens, Jeff McAffer, Richard S. Hall, Andy Piper, Hal Hildebrand, Glyn Normington, Martin Lippert, Gerd Wuetherich, and Bill Gallagher for their contributions in the development of this specification. |
|
来自: shaobin0604@1... > 《OSGi》