OSGi support
Since IPF version 1.6 you have the option to run IPF components and services on OSGi Release 4 platforms such as Equinox. Other platform should work as well but haven't been tested yet. All IPF components now contain a MANIFEST.MF file listing the provided and required packages along with other OSGi-specific information. In addition to exporting IPF packages for other bundles, IPF bundles also register shared platform services (like the mapping service or flow manager) at the OSGi service registry. IPF applications can now consume ready-to-use services from the OSGi service registry. IPF also provides specialized extender bundles that execute initialization tasks for other IPF platform and application bundles. These extender bundles activate DSL extensions that individual IPF bundles contribute. The concept behind extender bundles is described in the OSGi extender model. Distributing responsibility for service provisioning and execution of initialization tasks across specialized platform bundles makes the configuration of OSGi IPF applications much easier compared to non-OSGi IPF applications.
Scope
We've developed and tested IPF's OSGi support in context of a sample application that is described in the OSGi tutorial. Although it's a small sample application it is representative for many real-world applications in terms of used technologies i.e. Camel components and IPF features. The sample application makes use of the following Camel components.
- camel-osgi
- camel-core
- camel-spring
- camel-http
- camel-jms
And these are the IPF features that are used by the sample application
All components (OSGi bundles) providing these IPF features have been packaged and pre-configured with an Equinox OSGi platform. The complete package is referred to as IPF runtime and can be downloaded from here. The sample application can be readily deployed to it. The deployment process is explained in detail in the OSGi tutorial.
If you want to use further IPF features that are not listed above you'll need to deploy the corresponding IPF bundles including external dependencies to the OSGi container yourself or wait for one of the IPF 1.7 milestones that will follow the IPF 1.6 release. Especially, for the IPF event infrastructure we'll provide a nice fit into the OSGi environment by making use of the OSGi service registry and eventually the OSGi Event Admin Service.
Technology
For developing IPF's OSGi support we currently use
- Equinox as OSGi platform and
- Spring Dynamic Modules for adding Spring-support to OSGi
In the future we additionally plan to support
Any contributions here are highly welcome ![]()
Tooling
To generate MANIFEST.MF files for IPF platform and application bundles we use the Maven Bundle Plugin from the Apache Felix project.
Downloads
| Download | Latest Version | Description |
|---|---|---|
| IPF runtime | 1.6.0 | The IPF runtime package contains
|
| IPF release Maven repository | 1.6.0 | Released IPF bundles can be downloaded from this Maven repository |
| IPF snapshot Maven repository | 1.6.0 | Snapshot IPF bundles can be downloaded from this Maven repository |
| IPF OSGi release bundle repository | 1.6.0 | OBR view on released IPF bundles in the Maven repository. |
| IPF OSGi snapshot bundle repository | 1.6.0 | OBR view on snapshot IPF bundles in the Maven repository. |
IPF runtime installation and startup
To download the latest IPF runtime use this link directly or refer to the previous section. To install the downloaded runtime.zip unzip the archive to the current directory. To start the runtime on Windows, change to the created runtime folder and enter
runtime.bat
on the command line. If you're working on Linux use
runtime.sh
After about 10 seconds the startup should be complete. For an overview of the installed bundles and their status enter ss ('short status') in the console window. You should now see a list of 87 bundles as shown in the next figure.
Bundles with status ACTIVE are either bundles that registered services at the OSGi service registry, start some other services like a JMS broker or are extender bundles that listen to bundle lifecycle events and perform initialization and cleanup tasks. All other bundles should be in RESOLVED state. Now, you're ready to install and start the bundles of the sample application from the OSGi tutorial. To shutdown the IPF runtime, enter the close command in the console window.
Architecture
The following figure shows the high-level architecture of the IPF runtime and its relation to IPF applications.
- Basis for the IPF runtime is the Equinox OSGi platform. It comprises the Equinox system bundle and the Configuration Admin Service. The front-end to the Configuration Admin Service is the fileinstall bundle from Apache Felix project. This bundle reads configuration properties from properties files and configures others bundles via the Configuration Admin Service. We'll see an example later.
- A prerequisite for operating IPF bundles on an OSGi platform is the availability of all required external dependencies. These are bundles from the Spring Dynamic Modules project, the Apache Camel bundles and several other external dependencies. Most (but not all) of them are used as class-libraries i.e. they do not register services at the OSGi service registry. All external dependencies are contained in the plugins/ext folder of the IPF runtime.
- The IPF bundles together with all other bundles finally make up the IPF runtime. IPF bundles can be categorized based on how they interact with other bundles and the OSGi platform. This is summarized in the following section followed by a detailed description of the individual bundles.
- IPF applications are deployed to the OSGi platform that is part of the IPF runtime. This may require the deployment of additional Apache Camel and/or other bundles depending on your application details. For the OSGi tutorial all required dependencies are already provided by the IPF runtime.
Bundle categories
The following table gives an overview of bundle categories used for describing the IPF bundle architecture. These categories refer to the different kinds of interactions of an IPF bundle with other bundles and the OSGi platform. An individual bundle can belong to more than one category.
| Bundle Category | Symbol | Description |
|---|---|---|
| Library bundle | L | A bundle that is used by other bundles like class libraries. Library bundles don't register services at the OSGi service registry. |
| Service bundle | S | A bundle that consumes or registers one or more services at the OSGi service registry. Other bundles interact with that bundle via service interfaces. |
| Extender bundle | X | A bundle that perform initialization or cleanup tasks for other bundles. Extender bundles listen to lifecycle events of other bundles. |
| Extension bundle | N | A bundle that provides an IPF-specific extension. This extension is recognized and activated by one of the IPF extender bundles. |
| Configuration bundle | C | Configuration bundles are usually fragments that contain the Spring configuration files for components and services of their host bundles. A configuration bundle may also be a regular (i.e. non-fragment) bundle. The provided configurations are default configurations and can be further customized via the OSGi Configuration Admin Service. |
Bundle overview
This section gives an overview of IPF bundles and their categorization. For a more general overview of the functionality provided by these bundles refer to the IPF component architecture section. Some of the bundles are described more detailed in the following sections.
| Bundle | Bundle Category | Description |
|---|---|---|
| commons-core | L | A library bundle exporting packages that are imported by several other IPF bundles. |
| commons-flow | L,S | Registers flow management data access services (DAOs) at the OSGi service registry. These are used by platform-camel-flow |
| modules-hl7 | L,S,N | Registers a code mapping service at the OSGi registry and provides DSL extensions that make use of that mapping service. |
| modules-hl7dsl | L | A library bundle that export packages related to the HAPI DSL |
| platform-camel-core | L,N | A bundle providing the core features DSL extensions. |
| platform-camel-flow | L,S,N | A bundle providing the flow management DSL extensions. This bundle also registers the flow manager service at the OSGi service registry. |
| platform-camel-hl7 | L,N | A bundle providing the HL7 processing DSL extensions. |
| osgi-commons | L | Common classes for the IPF extender bundles. |
| osgi-extender-basic | X | An extender that loads and activates application-context-independent (static) DSL extensions provided by other bundles. |
| osgi-extender-spring | X | An extender that activates application-context-dependent (scoped) DSL extensions provided by other bundles. It is attached as fragment to the Spring Dynamic Modules extender bundle. |
| osgi-config-flow-manager | C | Default configuration for the services of the platform-camel-flow bundle. It is attached as fragment to platform-camel-flow. |
| osgi-config-flow-repository | C | Default configuration for the services of the commons-flow bundle. It is attached as fragment to commons-flow. |
| osgi-config-hl7 | C | Default configuration for the services of the commons-hl7 bundle. It is attached as fragment to commons-hl7. |
| osgi-config-jms | C,S | Default configuration for the ActiveMQ message broker and the Camel JMS component. The configured JMS component is registered as service in the OSGi service registry. Compared to all other configuration bundles this bundle is not a fragment. |
| osgi-config-log | C | Default logging configuration attached to the log4j bundle. |
Extender bundles
IPF extender bundles are listening for bundle lifecycle events and activate DSL extensions provided by other IPF bundles when these bundles are started. DSL extensions are either classes containing extension definitions or class-instances defined as Spring beans in a Spring application context. All IPF bundles that belong to bundle category N (see table in previous section) provide DSL extensions. DSL extension classes are loaded and activated by the osgi-extender-basic bundle whereas DSL extensions that are defined as beans in a Spring application context are activated by the osgi-extender-spring bundle. The following subsections explain these mechanisms in more detail.
Extension definition
Extension definitions recognized by the extender bundles are Groovy classes with either a class-scoped or instance-scoped extensions closure.
class MyExtensionClassA {
static extensions = {
// ... define extensions here
}
}
class MyExtensionClassB {
def extensions = {
// ... define extensions here
}
Extension classes must be added to the bundle's MANIFEST.MF. If you want the extender to load the extension class with the provider-bundle's class loader then you must add an Extension-Classes manifest header. The header value is a comma-sperated list of extension classes.
Extension-Classes: com.example.MyExtensionClassA,com.example.MyExtensionClassB
If the extensions closure has instance-scope then the containing class must provide a default constructor for instantiating the class. In both cases, the extension closure is finally called by the extender (via groovy.lang.Closure.call()).
If you want to define your extensions as bean in a Spring application context then you must add the name of the bean to the bundle's MANIFEST.MF. The header name for listing extension beans is Extension-Beans. The header value is a comma-separated list of bean names. Let's say you defined these two extension beans in your bundle's application context.
...
<bean id="myExtensionBeanX" class="...MyExtensionClassX">
...
</bean>
<bean id="myExtensionBeanY" class="...MyExtensionClassY">
...
</bean>
...
In order to be recognized by the extender their names must be added to the Extension-Beans manifest header.
Extension-Beans: myExtensionBeanX,myExtensionBeanY
| Activation of extensions Extensions are only recognized and activated by extenders if the providing bundles are started. |
Extension scope
Extension closures usually contain statements that extend other classes by means of Groovy meta-programming. For example, to introduce a blah DSL element into the IPF DSL we have to extend the org.apache.camel.model.ProcessorType class like in the following (incomplete) example.
import org.apache.camel.model.ProcessorType class MyExtensionClass { static extensions = { ProcessorType.metaClass.blah = {-> // ... } } }
Regardless which bundle this extension provides, after it has been activated by one of the IPF extender bundles the blah DSL extension is visible to all other bundles. This is helpful for DSL extensions provided by IPF itself but it also means that application-specific DSL extensions (i.e. extensions provided by IPF applications) are visible to all other applications. This could potentially lead to the problem that one application overwrites the DSL extensions of another application. In this case it makes sense to create a shared DSL extension bundle that is deployed (and activated) independently from other application bundles.
The osgi-extender-basic bundle
The osgi-extender-basic bundle inspects started bundles for presence of an Extension-Classes manifest header. The extension classes listed in the header are loaded using the target bundle's class loader and activated.
When the extender is started it currently doesn't inspect bundles that have been started before. So make sure that the extender is started before other bundles that contain extension classes.
The osgi-extender-spring fragment
The osgi-extender-spring bundle is a fragment bundle that customizes the Spring DM extender bundle. The fragment provides a post processor that becomes active after the Spring DM extender bundle started the application context of a target bundle.
After the application context has been started the post processor obtains a reference to beans listed in the Extension-Beans manifest header. All referenced extension beans are then activated.
Service bundles
The figure below outlines the IPF OSGi services architecture. It contains those IPF bundles that register services at the OSGi service registry or consume services from it. It also describes the interaction with the Configuration Admin Service. A sample application bundle was also included to illustrate its interactions with the IPF platform services. The arrows in this diagram denote service references.
IPF uses fragment bundles that contain default configuration files (Spring application context xml files) which are attached to their host bundles (see also section bundle overview). To customize the configuration you can replace a default configuration fragment (provided by IPF) with a customized one. Using this approach you can make any changes to the configuration you like (as long as this doesn't break the service contract of the host bundle). Another option is to change the service configuration via the Configuration Admin Service. A bundle however must be especially prepared for that. IPF provides a proof-of-principle implementation for this option with the commons-flow bundle. Changing the configuration of IPF bundles via the Configuration Admin Service is work in progress and support for it will be increased with upcoming IPF releases.
The following subsections describe the IPF service bundles in more detail. The configuration of IPF applications in an OSGi environment is described in the Application configuration section.
The commons-flow bundle
The commons-flow bundle implements the data access and persistence layer of the flow manager and registers all data access services at the OSGI service registry. The service interfaces are
- org.openehealth.ipf.commons.flow.repository.FlowRepository
- org.openehealth.ipf.commons.flow.repository.ConfigRepository
The commons-flow bundle also creates a transaction manager which can be used by other bundles for declarative and programmatic transaction demarcation. The transaction manager is registered as org.springframework.transaction.PlatformTransactionManager. The commons-flow bundle uses Hibernate for storing flow and configuration objects in a database. Since this is the only bundle in IPF that uses Hibernate we don't have the known issues with Hibernate in an OSGi environment.
The default configuration of the database and the data access services is attached as fragment to the commons-flow host bundle. The fragment bundle is osgi-config-flow-repository. The Spring configuration files are contained in the META-INF/spring folder of the fragment bundle. By default, a local (embedded) Derby database instance is created for storing flow and configuration objects. This can be customized via the Configuration Admin Service. You need to place a properties file named org.openehealth.ipf.osgi.config.flow.repository.cfg into the configuration/load folder of the IPF runtime. This file is read by the fileinstall bundle which pushes the configuration properties to the Configuration Admin Service. The persistence identity (PID) is derived from the filename and is org.openehealth.ipf.osgi.config.flow.repository. When the commons-flow bundle is started the configuration properties are received via the Configuration Admin Service. We use Spring Dynamic Modules' Configuration Admin Support for that interaction. The following table lists the properties that are currently supported for the org.openehealth.ipf.osgi.config.flow.repository.cfg file. If you omit one or more properties then their default value will be taken.
| Property | Default | Description |
|---|---|---|
| derby.deployment.mode | embedded | Defines the deployment mode of the flow manager's Derby database. You can either choose embedded or standalone. Use standalone if you want to connect to an existing (standalone) Derby Network Server. Use embedded if you want that IPF creates a database for you. If you choose standalone then the derby.standalone.* properties will be used for further configuration, otherwise, the derby.embedded.* properties will be used. |
| derby.embedded.name | workspace/derby | The relative or absolute path to the flow manager database. If no database exists at the specified location a new one will be created. |
| derby.standalone.name | data/flowmgr | The relative or absolute path to the flow manager database. Unless an absolte path is specified here, path names are relative to the Derby Network Server's location. |
| derby.standalone.host | localhost | Name of the Derby Network Server host. |
| derby.standalone.port | 1527 | Port of the Derby Network Server. |
The platform-camel-flow bundle
The platform-camel-flow bundle implements the flow management service which is accessible via the org.openehealth.ipf.commons.flow.FlowManager interface. It interacts with the data access services and the transaction manager exposed by the commons-flow bundle. The default configuration for the platform-camel-flow bundle is provided by the osgi-config-flow-manager fragment.
For replaying flows the platform-camel-flow bundle looks up org.openehealth.ipf.platform.camel.flow.ReplayStrategy objects from the OSGi service registry. In non-OSGi environments these objects are registered directly at the flow manager. In an OSGi environment we avoid a custom registry and use the OSGi service registry (following the OSGi whiteboard pattern). For instructions how applications should use flow management services in an OSGi environment refer to the Application Configuration section.
The modules-hl7 bundle
The modules-hl7 bundle registers a code mapping service at the OSGi service registry when the bundle is started. The default service configuration is contained in the osgi-config-hl7 fragment. By default the service has no code mapping definitions. These must be provided via one or more fragment bundles that are attached to the modules-hl7 bundle. The fragment must provide one or more mapping definition files under the META-INF/hl7 folder. The file names must follow the pattern mapping*.def. Here's an example.
mappings = {
gender(
F : 'W',
(ELSE) : { it }
)
// ... further mappings here
}
This example creates a mapping for the gender code F (english 'female') to W (german 'weiblich'). To attach the fragment containing this mapping definition to the modules-hl7 host bundle you must add the following manifest header to the fragment's MANIFEST.MF
Fragment-Host: org.openehealth.ipf.modules.modules-hl7
The osgi-config-jms bundle
The osgi-config-jms bundle contains the Spring configuration files for starting an ActiveMQ message broker and for registering a JMS component at the OSGi service registry. This component should be used by applications for interacting with the message broker. Compared to other osgi-config-* bundles the osgi-config-jms is not a fragment bundle.
Extension bundles
The platform-camel-core bundle
This bundle provides the Core DSL extensions. The DSL extensions are defined in the CoreModelExtension class as you can see from the bundle's Extension-Classes manifest header.
Extension-Classes: org.openehealth.ipf.platform.camel.core.extend.CoreModelExtension
The extensions are activated by the osgi-extender-basic bundle.
The platform-camel-flow bundle
This bundle provides the flow DSL extensions. The DSL extensions are defined in the FlowModelExtension class as you can see from the bundle's Extension-Classes manifest header.
Extension-Classes: org.openehealth.ipf.platform.camel.flow.extend.FlowModelExtension
The platform-camel-hl7 bundle
This bundle provides the HL7 DSL extensions. The DSL extensions are defined in the Hl7ModelExtension class as you can see from the bundle's Extension-Classes manifest header.
Extension-Classes: org.openehealth.ipf.platform.camel.hl7.extend.Hl7ModelExtension
The extensions are activated by the osgi-extender-basic bundle.
The modules-hl7 bundle
This bundle provides the HAPI extensions that make use of the mapping service described in a previous section. Using the example mapping definition from that section and after activation of the DSL extensions, any other bundle may invoke a mapGender() method on java.lang.String e.g.
assert 'W' == 'F'.mapGender()
This is achieved via Groovy meta-programming. For details refer to the HL7 processing tutorial. The DSL extensions are defined by a bean named hapiModelExtension in the application context of the osgi-config-hl7 fragment. The extension bean references the mapping service.
...
<bean id="mappingService"
class="org.openehealth.ipf.modules.hl7.mappings.BidiMappingService">
</bean>
<bean id="hapiModelExtension"
class="org.openehealth.ipf.modules.hl7.extend.HapiModelExtension">
<property name="mappingService" ref="mappingService" />
</bean>
...
The hapiModelExtension bean is added to the Extension-Beans manifest header and therefore recognized by the osgi-extender-spring bundle.
Extension-Beans: hapiModelExtension
Application configuration
IPF application configuration in an OSGi environment slightly differs from application configuration in other environments. The main differences are
- DSL extensions are activated by IPF extender bundles. There's no need to define an org.openehealth.ipf.platform.camel.core.extend.DefaultModelExtender any more. This applies to both, predefined DSL extensions as well as application-specific DSL extensions.
- Services required by applications (like the flow management service) are now provided by the IPF runtime. Service configuration and provisioning is done by dedicated IPF service bundles. Applications only need to reference these services from the OSGi service registry.
- Camel-components required by applications (like the flow management service) can also be provided by the IPF runtime. Applications only need to reference these components from the OSGi service registry. If you need a component configuration that is specific to your application you're still free to do so inside the Spring application context of your application bundle.
Application bundles also define their own Camel context. This is currently the recommended way to use Camel contexts in an OSGi environment. Having a Camel context provided as service and shared by several application bundles is currently not supported. Both approaches have their advantages and disadvantages and we'll probably support a shared Camel context in one of the upcoming IPF releases. In the following sub-sections you'll see some configuration examples how to use services and components provided by the IPF runtime.
Flow management
Applications that want to add flow management to their route definitions must obtain a reference to the flow manager service. Using Spring Dynamic Modules this can be done with the following bean definitions.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> ... <!-- Flow manager service reference. --> <osgi:reference id="flowManager" timeout="10000" interface="org.openehealth.ipf.commons.flow.FlowManager"> </osgi:reference> <!-- Needed for re-playing flows. --> <bean class="org.openehealth.ipf.platform.camel.flow.osgi.OsgiReplayStrategyRegistry" /> ... </beans>
This reference is necessary because the flow management DSL relies on a bean of type org.openehealth.ipf.commons.flow.FlowManager in the application context. The definition of the org.openehealth.ipf.platform.camel.flow.osgi.OsgiReplayStrategyRegistry is needed to support the re-play of flows. This registry is a proxy to the OSGi service registry and used by the DSL model classes to register flow interceptors (ReplayStrategy objects to be more accurate). Whenever a flow re-play is requested the flow manager tries to find a matching replay strategy in the OSGi service registry and uses it as starting point for the flow re-play. That's it! These two bean definitions plus the flow management DSL and you configured flow management for your IPF OSGi application.
Shared components
Camel components may be shared between application bundles where applicable. For example, the osgi-config-jms bundle from the IPF runtime configures a message broker and a JMS component for shared usage by IPF applications. If you want to make use of this component in your application you need to obtain it from the OSGi service registry. With Spring Dynamic Modules this can be done with following bean definition.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> ... <osgi:reference id="jms" bean-name="jms" timeout="10000" interface="org.apache.camel.Component"> </osgi:reference> ... </beans>
Now you can use the id of this bean (jms) as scheme in endpoint URIs (e.g. jms:queue:orders).
Route builder
Route definitions do not differ inside and outside an OSGi environment. For example, to use flow management and a JMS component (from the previous section) inside your route definitions you can simply write
package example import org.apache.camel.spring.SpringRouteBuilder class ExampleRouteBuilder extends SpringRouteBuilder { def replayIdentifier = this.class.package.name def applicationName = 'example' void configure() { from('...') .initFlow(replayIdentifier) // start flow management here .application(applicationName) // in context of this app name ... .to('jms:queue:orders') // write message to 'orders' queue // using shared JMS component from('jms:queue:orders') // read message to 'orders' queue .process { ... } .to(...) // deliver message to destination .ackFlow() // confirm successful delivery to // flow manager } }
Finally, add this route builder and a Camel context to the bundle's application context and you're done *).
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://activemq.apache.org/camel/schema/osgi" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://activemq.apache.org/camel/schema/osgi http://activemq.apache.org/camel/schema/osgi/camel-osgi.xsd"> <camel:camelContext id="camelContext" /> <bean id="routeBuilder" class="example.ExampleRouteBuilder"> </bean> </beans>
*) A description how to make your application a valid OSGi bundle is out of this sections's scope. For an example, refer to the OSGi tutorial.