A traditional Spring application uses either a single application context, or a parent context containing service layer, data layer, and domain objects with a child context containing web layer components. The application context may well be formed by aggregating the contents of multiple configuration files.
When deploying an application to OSGi the more natural structure is to package the application as a set of peer bundles (application contexts) interacting via the OSGi service registry. Independent subsystems should be packaged as independent bundles or sets of bundles (vertical partitioning). A subsystem may be package in a single bundle, or divided into several bundles partitioned by layer (horizontal partitioning). A straightforward web application may for example be divided into four modules (bundles): a web bundle, service layer bundle, data layer bundle, and domain model bundle. Such an application would look like this:
In this example the data layer bundle yields a data layer application context that contains a number of internal components (beans). Two of those beans are made publicly available outside of the application context by publishing them as services in the OSGi service registry.
The service layer bundle yields a service layer application context that contains a number of internal components (beans). Some of those components depend on data layer services, and import those services from the OSGi service registry. Two of the service layer components are made externally available as services in the OSGi service registry.
The web component bundle yields a web application context that contains a number of internal components (beans). Some of those components depend on application services, and import those services from the OSGi service registry. Since the domain model bundle contributes only domain model types, but does not need to create any components of its own, it has no associated application context.
Each application module should be packaged as an OSGi bundle. A
bundle is essentially a jar file with a
META-INF/MANIFEST.MF
file containing a series of
headers recognized by the OSGi Service Platform. See the OSGi Service
Platform Core Specification section 3.2 for details. Some OSGi
implementations may support exploded jar files, but the format remains
the same.
The Spring extender recognizes a bundle as "Spring-powered" and will create an associated application context when the bundle is started and one or both of the following conditions is true:
The bundle path contains a folder
META-INF/spring
with one or more files in that
folder with a '.xml' extension.
META-INF/MANIFEST.MF
contains a manifest
header Spring-Context
.
In addition, if the optional
SpringExtender-Version
header is declared in the
bundle manifest, then the extender will only recognize bundles where the
specified version constraints are satisfied by the version of the
extender bundle (Bundle-Version
). The value of the
SpringExtender-Version
header must follow the syntax
for a version range as specified in section 3.2.5 of the OSGi Service
Platform Core Specification.
In the absence of the Spring-Context
header the
extender expects every ".xml" file in the
META-INF/spring
folder to be a valid Spring
configuration file, and all directives (see below) take on their default
values.
An application context is constructed from this set of files. A suggested practice is to split the application context configuration into at least two files, named by convention modulename-context.xml and modulename-osgi-context.xml. The modulename-context.xml file contains regular bean definitions independent of any knowledge of OSGi. The modulename-osgi-context.xml file contains the bean definitions for importing and exporting OSGi services. It may (but is not required to) use the Spring Dynamic Modules OSGi schema as the top-level namespace instead of the Spring 'beans' namespace.
The Spring-Context
manifest header may be used
to specify an alternate set of configuration files. The resource paths
are treated as relative resource paths and resolve to entries defined in
the bundle and the set of attached fragments. When the
Spring-Context
header defines at least one
configuration file location, any files in
META-INF/spring
are ignored unless directly
referenced from the Spring-Context
header.
The syntax for the Spring-Context
header value
is:
Spring-Context-Value ::= context ( ',' context ) * context ::= path ( ';' path ) * (';' directive) *
This syntax is consistent with the OSGi Service Platform common header syntax defined in section 3.2.3 of the OSGi Service Platform Core Specification.
For example, the manifest entry:
Spring-Context: config/account-data-context.xml, config/account-security-context.xml
will cause an application context to be instantiated using the
configuration found in the files
account-data-context.xml
and
account-security-context.xml
in the bundle jar
file.
A number of directives are available for use with the
Spring-Context
header. These directives are:
create-asynchronously (false|true): controls whether the application context is created asynchronously (the default), or synchronously.
For example:
Spring-Context: *;create-asynchronously=false
Creates an application context synchronously, using all of the
"*.xml" files contained in the META-INF/spring
folder.
Spring-Context: config/account-data-context.xml;create-asynchrously:=false
Creates an application context synchronously using the
config/account-data-context.xml
configuration file.
Care must be taken when specifying synchronous context creation as the
application context will be created on the OSGi event thread, blocking
further event delivery until the context is fully initialized. If an
error occurs during the synchronous creation of the application context
then a FrameworkEvent.ERROR
event is raised. The bundle will still
proceed to the ACTIVE
state.
wait-for-dependencies (true|false): controls whether or not application context creation should wait for any mandatory service dependencies to be satisfied before proceeding (the default), or proceed immediately without waiting if dependencies are not satisfied upon startup.
For example:
Spring-Context: config/osgi-*.xml;wait-for-dependencies:=false
Creates an application context using all the files matching "osgi-*.xml" in the config directory. Context creation will begin immediately even if dependencies are not satisfied. This essentially means that mandatory service references are treated as though they were optional - clients will be injected with a service object that may not be backed by an actual service in the registry initially. See section 5.2 for more details.
timeout (300): the time to wait (in
seconds) for mandatory dependencies to be satisfied before giving up
and failing application context creation. This setting is ignored if
wait-for-dependencies:=false
is specified. The
default is 5 minutes (300 seconds).
For example:
Spring-Context: *;timeout:=60
Creates an application context that waits up to 1 minute (60 seconds) for its mandatory dependencies to appear.
publish-context (true|false): controls whether or not the application context object itself should be published in the OSGi service registry. The default is to publish the context.
For example:
Spring-Context: *;publish-context:=false
If there is no Spring-Context manifest entry, or no value is specified for a given directive in that entry, then the directive takes on its default value.
The Spring Dynamic Modules project provides an number of bundle artifacts that must be installed in your OSGi platform in order for the Spring extender to function correctly:
The extender bundle itself,
org.springframework.osgi.extender
The core implementation bundle for the Spring Dynamic Modules
support, org.springframework.osgi.core
The Spring Dynamic Modules I/O support library bundle, '
org.springframework.osgi.io
In addition the Spring Framework provides a number of bundles that are required to be installed. As of release 2.5 of the Spring Framework, the Spring jars included in the Spring distribution are valid OSGi bundles and can be installed directly into an OSGi platform. The minimum required set of bundles is:
spring-core.jar (bundle symbolic name
org.springframework.bundle.spring.core
)
spring-context.jar (bundle symbolic name
org.springframework.bundle.spring.context
)
spring-beans.jar (bundle symbolic name
org.springframework.bundle.spring.beans
)
spring-aop.jar (bundle symbolic name
org.springframework.bundle.spring.aop
)
In additional the following supporting library bundles are required. OSGi-ready versions of these libraries are shipped with the Spring Dynamic Modules distribution.
aopalliance
backport-util (when running on JDK 1.4)
cglib-nodep (when proxying classes rather then interfaces, needed in most cases)
commons-logging API (SLF4J version highly recommended)
logging implementation such as log4j
Refer to the OSGi Service Platform for details of the
Import-Package
and Export-Package
manifest headers. Your bundle will need an
Import-Package
entry for every external package that
the bundle depends on. If your bundle provides types that other bundles
need access to, you will need Export-Package
entries
for every package that should be available from outside of the
bundle.
Many enterprise application libraries assume that all of the types and resources that comprise the application are accessible through the context class loader. While most developers do not use the context class loader, the loader is used heavily by application servers, containers or applications that are multi-threaded.
In OSGi R4, the set of types and resources available through the context class loader is undefined. This means that the OSGi platform does not make a guarantee of the thread context class loader value or in other words, it does not manage it.
Thus code (for example libraries) that performs manual class loading or that generates new classes dynamically can cause problems when executed inside an OSGi environment.
Spring Dynamic Modules guarantees that during the creation of an application context on behalf of a given bundle, all of the types and resources on the bundle's classpath are accessible via the context class loader. Spring Dynamic Modules also allows you to control what is accessible through the context class loader when invoking external services and when servicing requests on exported services. See section 5 for details on this.
Work is underway in the OSGi R5 timeframe to provide standardized
support for dealing with generated classes and implicit class path
dependencies introduced by third-party libraries. In the interim you may
need to rely on workarounds such as the
DynamicImport-Package
manifest header, or the
facilities provided by specific OSGi implementations such as Equinox's
buddy mechanism. The Spring Dynamic Modules documentation contains more
details on known issues with common enterprise libraries and the
workarounds.
Your chosen OSGi platform implementation should be able to provide
you with a good deal of information about the current status of the OSGi
environment. For example, starting Equinox with the
-console
argument provides a command-line console
through which you can determine which bundles are installed and their
states, the packages and services exported by bundles, find out why a
bundle has failed to resolve, and drive bundles through the
lifecycle.
In addition, Spring itself and the Spring Dynamic Modules bundles
contain extensive logging instrumentation that can help you diagnose
problems. The recommended approach is to deploy the Simple Logging
Facade for Java (slf4j)
slf4j-api.jar and slf4j-log4j13.jar bundles (the jar files distributed
by the project are valid OSGi bundles). Then you simply need to create a
log4j.properties
file in the root of your bundle
classpath.
Note that Spring Dynamic Modules uses commons-logging API internally which means that its logging implementation is fully pluggable. Please see the FAQ and Resources pages for more information on other logging libraries besides log4j.