Version 4.4.1
Copyright © 2011-2013 Red Hat, Inc. and/or its affiliates.
Updated: 06 Jun 2013
Table of Contents
List of Figures
List of Tables
List of Examples
The OSGi specification supports modular application development by defining a runtime framework that simplifies building, deploying, and managing complex applications.
Fuse ESB 4 still supports JBI, as described in Using Java Business Integration. However, because Fuse ESB 4 is based on OSGi, it provides additional flexibility. Fuse ESB 4 has the following layered architecture:
Technology layer—includes technologies such as JBI, JAX-WS, JAX-RS, JMS, Spring, and JEE
Fuse ESB kernel —a wrapper layer around the OSGi container implementation, which provides support for deploying the OSGi container as a runtime server. Runtime features provided by the Fuse ESB kernel include hot deployment, management, and administration features.
OSGi framework —implements OSGi functionality, including managing dependencies and bundle lifecycles
Figure 1.1 shows the architecture of Fuse ESB kernel.
Fuse ESB kernel is based on Apache Karaf, a powerful, lightweight, OSGi-based runtime container for deploying and managing bundles to facilitate componentization of applications. Fuse ESB kernel also provides native OS integration and can be integrated into the operating system as a service so that the lifecycle is bound to the operating system.
As shown in Figure 1.1, Fuse ESB kernel extends the OSGi layers with:
Console—extensible Gogo console manages services, installs and manages applications and libraries, and interacts with the Fuse ESB kernel runtime. See the Console Reference.
Logging—powerful, unified logging subsystem provides console commands to display, view and change log levels. See Configuring and Running Fuse ESB Enterprise.
Provisioning—provides multiple mechanisms for installing applications and libraries. See Deploying Features.
Deployer—supports hot deployment of OSGi bundles. When you update or delete bundles, the changes are made automatically. See Hot Deployment.
Admin—provides console commands to administer instances of Fuse ESB kernel. See Configuring and Running Fuse ESB Enterprise.
Spring DM—simplifies building Spring applications that run in an OSGi framework.
Blueprint—is essentially a standardized version of Spring DM. It is a dependency injection framework that simplifies interaction with the OSGi container—for example, providing standard XML elements to import and export OSGi services.
The OSGi Alliance is an independent organization responsible for defining the features and capabilities of the OSGi Service Platform Release 4. The OSGi Service Platform is a set of open specifications that simplify building, deploying, and managing complex software applications.
OSGi technology is often referred to as the dynamic module system for Java. OSGi is a framework for Java that uses bundles to modularly deploy Java components and handle dependencies, versioning, classpath control, and class loading. OSGi's lifecycle management allows you to load, start, and stop bundles without shutting down the JVM.
OSGi provides the best runtime platform for Java, a superior class loading architecture, and a registry for services. Bundles can export services, run processes, and have their dependencies managed. Each bundle can have its requirements managed by the OSGi container.
Fuse ESB kernel uses Equinox as its default OSGi implementation. Fuse ESB kernel can also use any standard OSGi container, such as Apache Felix. The framework layers form the container where you install bundles. The framework manages the installation and updating of bundles in a dynamic, scalable manner, and manages the dependencies between bundles and services.
As shown in Figure 1.1, the OSGi framework contains the following:
Bundles — Logical modules that make up an application. See OSGi Bundles.
Service layer — Provides communication among modules and their contained components. This layer is tightly integrated with the lifecycle layer. See OSGi Services.
Lifecycle layer — Provides access to the underlying OSGi framework. This layer handles the lifecycle of individual bundles so you can manage your application dynamically, including starting and stopping bundles.
Module layer — Provides an API to manage bundle packaging, dependency resolution, and class loading.
Execution environment — A configuration of a JVM. This environment uses profiles that define the environment in which bundles can work.
Security layer — Optional layer based on Java 2 security, with additional constraints and enhancements.
Each layer in the framework depends on the layer beneath it. For example, the lifecycle layer requires the module layer. The module layer can be used without the lifecycle and service layers.
An OSGi service is a Java class or service interface with service properties defined as name/value pairs. The service properties differentiate among service providers that provide services with the same service interface.
An OSGi service is defined semantically by its service interface, and it is implemented as a service object. A service's functionality is defined by the interfaces it implements. Thus, different applications can implement the same service.
Service interfaces allow bundles to interact by binding interfaces, not implementations. A service interface should be specified with as few implementation details as possible.
In the OSGi framework, the service layer provides communication between bundles and their contained components using the publish, find, and bind service model. The service layer contains a service registry where:
Service providers register services with the framework to be used by other bundles
Service requesters find services and bind to service providers
Services are owned by, and run within, a bundle. The bundle registers an implementation of a service with the framework service registry under one or more Java interfaces. Thus, the service’s functionality is available to other bundles under the control of the framework, and other bundles can look up and use the service. Lookup is performed using the Java interface and service properties.
Each bundle can register multiple services in the service registry using the fully qualified name of its interface and its properties. Bundles use names and properties with LDAP syntax to query the service registry for services.
A bundle is responsible for runtime service dependency management activities including publication, discovery, and binding. Bundles can also adapt to changes resulting from the dynamic availability (arrival or departure) of the services that are bound to the bundle.
Service interfaces are implemented by objects created by a bundle. Bundles can:
Register services
Search for services
Receive notifications when their registration state changes
The OSGi framework provides an event notification mechanism so service requesters can receive notification events when changes in the service registry occur. These changes include the publication or retrieval of a particular service and when services are registered, modified, or unregistered.
When a bundle wants to use a service, it looks up the service and invokes the Java object as a normal Java call. Therefore, invocations on services are synchronous and occur in the same thread. You can use callbacks for more asynchronous processing. Parameters are passed as Java object references. No marshalling or intermediary canonical formats are required as with XML. OSGi provides solutions for the problem of services being unavailable.
In addition to your own services, the OSGi framework provides the following optional services to manage the operation of the framework:
Package Admin service—allows a management agent to define the policy for managing Java package sharing by examining the status of the shared packages. It also allows the management agent to refresh packages and to stop and restart bundles as required. This service enables the management agent to make decisions regarding any shared packages when an exporting bundle is uninstalled or updated.
The service also provides methods to refresh exported packages that were removed or updated since the last refresh, and to explicitly resolve specific bundles. This service can also trace dependencies between bundles at runtime, allowing you to see what bundles might be affected by upgrading.
Start Level service—enables a management agent to control the starting and stopping order of bundles. The service assigns each bundle a start level. The management agent can modify the start level of bundles and set the active start level of the framework, which starts and stops the appropriate bundles. Only bundles that have a start level less than, or equal to, this active start level can be active.
URL Handlers service—dynamically extends the Java runtime with URL schemes and content handlers enabling any component to provide additional URL handlers.
Permission Admin service—enables the OSGi framework management agent to administer the permissions of a specific bundle and to provide defaults for all bundles. A bundle can have a single set of permissions that are used to verify that it is authorized to execute privileged code. You can dynamically manipulate permissions by changing policies on the fly and by adding new policies for newly installed components. Policy files are used to control what bundles can do.
Conditional Permission Admin service—extends the Permission Admin service with permissions that can apply when certain conditions are either true or false at the time the permission is checked. These conditions determine the selection of the bundles to which the permissions apply. Permissions are activated immediately after they are set.
The OSGi framework services are described in detail in separate chapters in the OSGi Service Platform Release 4 specification available from the release 4 download page on the OSGi Alliance web site.
In addition to the OSGi framework services, the OSGi Alliance defines a set of optional, standardized compendium services. The OSGi compendium services provide APIs for tasks such as logging and preferences. These services are described in the OSGi Service Platform, Service Compendium available from the release 4 download page on the OSGi Alliance Web site.
The Configuration Admin compendium service is like a central hub that persists configuration information and distributes it to interested parties. The Configuration Admin service specifies the configuration information for deployed bundles and ensures that the bundles receive that data when they are active. The configuration data for a bundle is a list of name-value pairs. See Fuse ESB kernel.
With OSGi, you modularize applications into bundles. Each bundle is a tightly coupled, dynamically loadable collection of classes, JARs, and configuration files that explicitly declare any external dependencies. In OSGi, a bundle is the primary deployment format. Bundles are applications that are packaged in JARs, and can be installed, started, stopped, updated, and removed.
OSGi provides a dynamic, concise, and consistent programming model for developing bundles. Development and deployment are simplified by decoupling the service's specification (Java interface) from its implementation.
The OSGi bundle abstraction allows modules to share Java classes. This is a static form of reuse. The shared classes must be available when the dependent bundle is started.
A bundle is a JAR file with metadata in its OSGi manifest file. A bundle contains class files and, optionally, other resources and native libraries. You can explicitly declare which packages in the bundle are visible externally (exported packages) and which external packages a bundle requires (imported packages).
The module layer handles the packaging and sharing of Java packages between bundles and the hiding of packages from other bundles. The OSGi framework dynamically resolves dependencies among bundles. The framework performs bundle resolution to match imported and exported packages. It can also manage multiple versions of a deployed bundle.
OSGi uses a graph model for class loading rather than a tree model (as used by the JVM). Bundles can share and re-use classes in a standardized way, with no runtime class-loading conflicts.
Each bundle has its own internal classpath so that it can serve as an independent unit if required.
The benefits of class loading in OSGi include:
Sharing classes directly between bundles. There is no requirement to promote JARs to a parent class-loader.
You can deploy different versions of the same class at the same time, with no conflict.
Figure 1.2 shows an overview of the OSGi transaction architecture in Fuse ESB. The core of the architecture is a JTA transaction manager based on Apache Geronimo, which exposes various transaction interfaces as OSGi services.
The JTA Transaction Services Specification section of the OSGi Service Platform Enterprise Specification describes the kind of transaction support that can (optionally) be provided by an OSGi container. Essentially, OSGi mandates that the transaction service is accessed through the Java Transaction API (JTA).
The transaction service exports the following JTA interfaces as OSGi services (the JTA services):
javax.transaction.UserTransaction
javax.transaction.TransactionManager
javax.transaction.TransactionSynchronizationRegistry
Only one JTA provider should be made available in an OSGi container. In other words, the JTA services are registered only once and the objects obtained by importing references to the JTA services must be unique.
The Fuse ESB transaction service exports the following additional interfaces as OSGi services:
org.springframework.transaction.PlatformTransactionManager
org.apache.geronimo.transaction.manager.RecoverableTransactionManager
By obtaining a reference to the PlatformTransactionManager OSGi service,
it is possible to integrate application bundles written using the Spring transaction API
into the Fuse ESB transaction architecture.
Fuse ESB provides an OSGi-compliant implementation of the transaction service in the following bundle (which is installed in the OSGi container by default):
org.apache.aries.transaction.manager
The Fuse ESB transaction bundle exports exports a variety of transaction interfaces as OSGi services (making them available to other bundles in the container), as follows:
JTA interfaces—the JTA UserTransaction,
TransactionManager, and
TransactionSynchronizationRegistry interfaces are exported, as
required by the OSGi transaction specification.
Spring transaction interface—the Spring
PlatformTransactionManager interface is exported, in order to
facilitate bundles that are written using the Spring transaction APIs.
The PlatformTransactionManager OSGi service and the JTA services access
the same underlying transaction manager.
The underlying implementation of the Fuse ESB transaction service is provided by the Apache Geronimo transaction manager. Apache Geronimo is a full implementation of a J2EE server and, as part of the J2EE implementation, Geronimo has developed a sophisticated transaction manager with the following features:
Support for enlisting multiple XA resources.
Support for 1-phase and 2-phase commit protocols.
Support for suspending and resuming transactions.
Support for automatic transaction recovery upon startup.
The transaction recovery feature depends on a transaction log, which records the status of all pending transactions in persistent storage.
The implementation of the transaction log is provided by HOWL, which is a high speed persistent logger that is optimized for XA transaction logs.
Normally, it is recommended that application bundles access the transaction service through the JTA interface. To use the transaction service in a JTA-based application bundle, import the relevant JTA service as an OSGi service and use the JTA service to begin, commit, or rollback transactions.
If you have already implemented an application bundle using the Spring transaction
API, you might find it more convenient to access the transaction service through the
Spring API (represented by the PlatformTransactionManager Java interface).
This means that you are able to deploy the same source code either in a pure Spring
container or in an OSGi container, by changing only the configuration snippet that
obtains a reference to the transaction service.
In order to enlist multiple resources in the same transaction (where a transaction is
committed through the 2-phase commit protocol), it is necessary for each participating
resource to provide an XA switch. An XA switch is an object that
enables the transaction manager to control a resource manager through the XA
interface, javax.transaction.xa.XAResource.
The JTA provides a method for enlisting XAResource objects in the
current transaction. Using this approach, it is possible to enlist multiple XA resources
in the current transaction.
The standard JTA approach to enlisting XA resources is to add the XA resource to the
current javax.transaction.Transaction object (representing the current
transaction). In other words, you must explicitly enlist an XA resource every
time a new transaction starts.
If you are familiar with how transactions work in J2EE containers, you might find this approach surprising: a J2EE container automatically enlists the relevant XA resource in the current transaction whenever you access a data source. In contrast to the J2EE approach, however, the OSGi container does not automatically enlist XA resources in the current transaction. You must write the code to enlist the XA resources yourself or implement a custom wrapper layer.
In the case of JMS XA resources, Fuse Message Broker provides an XA pool wrapper class that implements auto-enlistment for you. Use the following JMS connection factory class:
org.apache.activemq.pool.XaPooledConnectionFactoryImplements JMS pooled connections and auto-enlistment of the JMS XA
resource. This class is provided by the activemq-pool Maven
artifact.
If you are using a JMS implementation other than Fuse Message Broker, you could use the XA
pool connection factory from the Jencks project,
org.jencks.pool.PooledSpringXAConnectionFactory. This connection
factory is suitable for generic JMS implementations.
Fuse Message Broker supports the standard JMS framework for managing transactions. The approach to managing transactions in JMS depends on whether you use local transactions (where the broker is the only participating resource) or global transactions (where multiple resources participate in the transaction), as follows:
Local transactions—transactions involving the
receiving or sending of messages are demarcated using the commit() or
rollback() methods of the javax.jms.Session object
(assuming that the current JMS session is marked as transacted).
Global transactions—the JMS client must use the
ActiveMQXAConnectionFactory JMS connection factory to access the
broker in this case. This connection factory enables you to create
javax.jms.XASession session objects, which provide an XA resource
through the getXAResource() method. You can then enlist the XA
resource in the current JTA transaction and use the
javax.transaction.TransactionManager interface to demarcate
transactions.
Through the ActiveMQXAConnectionFactory JMS connection factory, Fuse Message Broker
can provide an XA resource that represents the underlying message broker. This means
that messages will not be added to or removed from the broker's persistent storage,
unless the current transaction completes successfully. The following broker persistence
layers provide full support for the XA protocol (including a persistent transaction log
that enables recovery):
AMQ
Journal
KahaDB
To obtain a reference to the Fuse Message Broker XA resource, you must use the
ActiveMQXAConnectionFactory JMS connection factory to access the
broker.
Fuse Mediation Router provides a Camel JMS component, which is capable of integrating with the JTA transaction service with full support for XA transactions. In order to support XA transactions, you must configure the Camel JMS component's JMS connection factory and transaction manager as follows:
Connection factory—use the Apache ActiveMQ
XaPooledConnectionFactory class to wrap an Apache ActiveMQ
ActiveMQXAConnectionFactory instance.
Transaction manager—use the Spring
JtaTransactionManager class to wrap the JTA
UserTransaction and TransactionManager instances
(alternatively, you could just reference the
PlatformTransactionManager instance exported by the Fuse ESB
transaction manager implementation—see Spring transaction integration).
In principle, you can replace the default transaction manager with an alternative transaction manager, but this requires you to write a significant amount of custom code. If you want to replace the Geronimo transaction manager with a different transaction manager implementation, you could do so as follows:
Uninstall the default OSGi transaction service bundle,
org.apache.aries.transaction.manager.
Implement your own transaction service bundle, which exports the standard JTA interfaces as OSGi services (see OSGi mandated transaction architecture).
Install your custom transaction service bundle into the OSGi container.
For example, a suitable alternative transaction manager is Atomikos TransactionEssentials, which is another Open Source, Apache-licensed transaction manager. But this transaction manager does not currently provide its own OSGi integration layer, so you would have to implement that yourself.
The following specifications are relevant to the transaction architecture in Fuse ESB:
OSGi transaction specification—in section 123 JTA Transaction Services Specification v1.0 from the OSGi Service Platform Enterprise Specification v4.2
Spring transactions API—see the Transaction Management chapter from the current Spring Reference Manual.
Maven is an open source build system which is available from the Apache Maven project. In principle, you could use any build system to build an OSGi bundle. But Maven is strongly recommended for the following reasons. Firstly, because Maven is used to build the Fuse ESB product itself, all of the Maven artifacts that you need to build your own bundle project are likely to be already available from the fusesource Maven repository or from the Maven central repository. Secondly, Apache Felix provides a Maven bundle plug-in, which automates the process of packaging code as an OSGi bundle. Thirdly, Fuse ESB provides a collection of Maven archetypes, which can generate starting point code for your project, enabling you to get started rapidly.
One of the most important principles of the Maven build system is that there are
standard locations for all of the files in the Maven
project. There are several advantages to this principle. One advantage is that Maven
projects normally have an identical directory layout, making it easy to find files
in a project (once you are familiar with the standard layout). Another advantage is
that the various tools integrated with Maven need almost no
initial configuration. For example, the Java compiler knows that it should compile
all of the source files under src/main/java and put the results into
target/classes.
Example 2.1 shows the elements of the standard Maven directory layout that are relevant to building OSGi bundle projects. In addition, the standard locations for Spring-DM and Blueprint configuration files (which are not defined by Maven) are also shown.
Example 2.1. Standard Maven Directory Layout
ProjectDir/
pom.xml
src/
main/
java/
...
resources/
META-INF/
spring/
*.xml
OSGI-INF/
blueprint/
*.xml
test/
java/
resources/
target/
...It is, in fact, possible to override the standard directory layout, but this is not a recommended practice in Maven.
The pom.xml file is the Project Object Model (POM) for the current
project, which contains a complete description of how to build the current project.
A pom.xml file can be completely self-contained, but frequently
(particular for more complex Maven projects) it can import settings from a
parent POM file.
The src/ directory contains all of the code and resource files that
you will work on while developing the project.
The target/ directory contains the result of the build (typically a
JAR file), as well as all all of the intermediate files generated during the build.
For example, after performing a build, the target/classes/ directory
will contain a copy of the resource files and the compiled Java classes.
The main/ directory contains all of the code and resources needed for
building the artifact.
The test/ directory contains all of the code and resources for
running unit tests against the compiled artifact.
The java/ directory contains Java source code (*.java
files) with the standard Java directory layout (that is, where the directory
pathnames mirror the Java package names, with / in place of the
. character). The src/main/java/ directory contains
the bundle source code and the src/test/java/ directory contains the
unit test source code.
If you have any configuration files, data files, or Java properties to include in
the bundle, these should be placed under the src/main/resources/
directory. The files and directories under src/main/resources/ will be
copied into the root of the JAR file that is generated by the Maven build
process.
The files under src/test/resources/ are used only during the testing
phase and will not be copied into the generated JAR
file.
By default, Fuse ESB installs and activates support for Spring Dynamic Modules (Spring
DM), which integrates Spring with the OSGi container. This means that it
is possible for you to include Spring configuration files,
META-INF/spring/*.xml, in your bundle. One of the key consequences
of having Spring DM enabled in the OSGi container is that the lifecycle of the
Spring application context is automatically synchronized with the OSGi bundle
lifecycle:
Activation—when a bundle is activated, Spring
DM automatically scans the bundle to look for Spring configuration files in
the standard location (any .xml files found under the
META-INF/spring/ directory). If any Spring files are found,
Spring DM creates an application context for the bundle and creates the
beans defined in the Spring configuration files.
Stopping—when a bundle is stopped, Spring DM automatically shuts down the bundle's Spring application context, causing any Spring beans to be deleted.
In practice, this means that you can treat your Spring-enabled bundle as if it is being deployed in a Spring container. Using Spring DM, the features of the OSGi container and a Spring container are effectively merged. In addition, Spring DM provides additional features to support the OSGi container environment—some of these features are discussed in Deploying an OSGi Service.
OSGi R4.2 defines a blueprint container, which is effectively a standardized
version of Spring DM. Fuse ESB has built-in support for the blueprint container, which
you can enable simply by including blueprint configuration files,
OSGI-INF/blueprint/*.xml, in your project. For more details about
the blueprint container, see Deploying an OSGi Service.
The simplest way to get started with Maven is to generate a new project using a Maven archetype. This section describes how to set up Maven to use the Fuse ESB archetypes, which can generate starting points for different kinds of bundle application.
In order to build a project using Maven, you must have the following prerequisites:
Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from the Maven download page (minimum version is 2.2.1).
Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
In order to access artifacts from the ESB Maven repository, you need
to add the fusesource repository to Maven's settings.xml
file. Maven looks for your settings.xml file in the following standard
location:
UNIX:
home/User/.m2/settings.xml
Windows:
Documents and
Settings\User\.m2\settings.xml
If there is currently no settings.xml file at this location, you need
to create a new settings.xml file. Modify the settings.xml
file by adding the repository element for fusesource, as
shown in bold text in the following example:
<settings>
<profiles>
<profile>
<id>my-profile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>fusesource</id>
<url>http://repo.fusesource.com/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>fusesource.snapshot</id>
<url>http://repo.fusesource.com/maven2-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>apache-public</id>
<url>https://repository.apache.org/content/groups/public/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
...
</repositories>
</profile>
</profiles>
...
</settings>The preceding example also shows repository element for the following repositories:
fusesource-snapshot repository—if you want to
experiment with building your application using an Fuse ESB snapshot kit, you
can include this repository.
apache-public repository—you might not always need this
repository, but it is often useful to include it, because Fuse ESB depends on
many of the artifacts from Apache.
The basic building block in the Maven build system is an
artifact. When using Maven to build an OSGi project,
there is a natural mapping between an OSGi bundle and a Maven artifact: a bundle is
simply an artifact whose packaging type is set to bundle.
A key aspect of Maven functionality is the ability to locate artifacts and manage
the dependencies between them. Maven defines the location of an artifact using the
system of Maven coordinates, which uniquely define the
location of a particular artifact. A basic coordinate tuple has the form,
{. Sometimes Maven augments the basic
set of coordinates with the additional coordinates,
groupId,
artifactId,
version}packaging and classifier.
A tuple can be written with the basic coordinates, or with the additional
packaging coordinate, or with the addition of both
the packaging and classifier
coordinates, as follows:
groupdId:artifactId:versiongroupdId:artifactId:packaging:versiongroupdId:artifactId:packaging:classifier:version
Each coordinate can be explained as follows:
groupdIdDefines a scope for the name of the artifact. You would typically use
all or part of a package name as a group ID—for example,
org.fusesource.example.
artifactIdDefines the artifact name (relative to the group ID).
versionSpecifies the artifact's version. A version number can have up to four
parts: n.n.n.n, where the last part of the version number
can contain non-numeric characters (for example, the last part of
1.0-SNAPSHOT is the alphanumeric substring,
0-SNAPSHOT).
packagingDefines the packaged entity that is produced when you build the
project. For OSGi projects, the packaging is bundle. The
default value is jar.
classifierEnables you to distinguish between artifacts that were built from the same POM, but have different content.
The group ID, artifact ID, packaging, and version are defined by the corresponding elements in an artifact's POM file. For example:
<project ... > ... <groupId>org.fusesource.example</groupId> <artifactId>bundle-demo</artifactId> <packaging>bundle</packaging> <version>1.0-SNAPSHOT</version> ... </project>
For example, to define a dependency on the preceding artifact, you could add the
following dependency element to a POM:
<project ...>
...
<dependencies>
<dependency>
<groupId>org.fusesource.example</groupId>
<artifactId>bundle-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
...
</project>It is not necessary to specify the bundle
package type in the preceding dependency, because a bundle is just a particular
kind of JAR file and jar is the default Maven package type. If you
do need to specify the packaging type explicitly in a dependency, however, you
can use the type element.
To help you get started quickly, you can invoke a Maven archetype to generate the initial outline of a Maven project (a Maven archetype is analogous to a project wizard). The following Maven archetypes can generate projects for building OSGi bundles:
The Fuse Service Framework code-first archetype creates a project for building a service from
Java. To generate a Maven project with the coordinates,
GroupId:ArtifactId:Version,
enter the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-cxf-code-first-osgi-bundle -DarchetypeVersion=2011.02.1-fuse-00-08 -DgroupId=GroupId-DartifactId=ArtifactId-Dversion=Version
The arguments to the mvn command are shown on separate lines
purely for the sake of readability. When you are entering the command at a
command prompt, you must ensure that all of the parameters are on the same
line.
The Fuse Service Framework WSDL-first archetype creates a project for building a service from
WSDL. To generate a Maven project with the coordinates,
GroupId:ArtifactId:Version,
enter the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-cxf-wsdl-first-osgi-bundle -DarchetypeVersion=2011.02.1-fuse-00-08 -DgroupId=GroupId-DartifactId=ArtifactId-Dversion=Version
The Fuse Mediation Router OSGi archetype creates a project for building a route that can be
deployed into the OSGi container. To generate a Maven project with the coordinates,
GroupId:ArtifactId:Version,
enter the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-camel-osgi-bundle -DarchetypeVersion=2011.02.1-fuse-00-08 -DgroupId=GroupId-DartifactId=ArtifactId-Dversion=Version
By default, the preceding archetypes create a project in a new directory, whose
names is the same as the specified artifact ID,
ArtifactId. To build the bundle defined by the new
project, open a command prompt, go to the project directory (that is, the directory
containing the pom.xml file), and enter the following Maven
command:
mvn install
The effect of this command is to compile all of the Java source files, to generate
a bundle JAR under the ArtifactId/target
directory, and then to install the generated JAR in the local Maven
repository.
You can skip the final build step (installing the generated JAR in the local
Maven repository) by entering the mvn package command. But only do
this, if you are sure that no other local Maven projects
depend on this artifact.
If you already have a Maven project and you want to modify it so that it generates an OSGi bundle, perform the following steps:
Configure Maven to generate an OSGi bundle by changing the package type to
bundle in your project's pom.xml file. Change the
contents of the packaging element to bundle, as shown in
the following example:
<project ... >
...
<packaging>bundle</packaging>
...
</project>The effect of this setting is to select the Maven bundle plug-in,
maven-bundle-plugin, to perform packaging for this project. This
setting on its own, however, has no effect until you explicitly add the bundle
plug-in to your POM.
To add the Maven bundle plug-in, copy and paste the following sample
plugin element into the project/build/plugins section
of your project's pom.xml file:
<project ... >
...
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>Where the bundle plug-in is configured by the settings in the
instructions element. For a general guide to customizing bundle
instructions, see Configuring the Bundle Plug-In, and for specific
recommendations, see Packaging a Web Service in a Bundle and Packaging Routes in a Bundle.
It is almost always necessary to specify the JDK version in your POM file. If your
code uses any modern features of the Java language—such as generics, static
imports, and so on—and you have not customized the JDK version in the POM,
Maven will fail to compile your source code. It is not
sufficient to set the JAVA_HOME and the PATH environment
variables to the correct values for your JDK, you must also modify the POM
file.
To configure your POM file, so that it accepts the Java language features
introduced in JDK 1.5, add the following maven-compiler-plugin plug-in
settings to your POM (if they are not already present):
<project ... >
...
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
...
</project>If you are using JDK 1.6, you can change the contents of the source
element and the target element to 1.6. But this has the
drawback that you would be unable to deploy the artifact on JDK 1.5. Typically, it
is more convenient to build on JDK 1.5, which produces an artifact that can be
deployed either on JDK 1.5 or on JDK 1.6.
A bundle plug-in requires very little information to function. All of the required properties use default settings to generate a valid OSGi bundle.
While you can create a valid bundle using just the default values, you will probably
want to modify some of the values. You can specify most of the properties inside the
plug-in's instructions element.
Some of the commonly used configuration properties are:
By default, the bundle plug-in sets the value for the
Bundle-SymbolicName property to
groupId + "." +
artifactId, with the following exceptions:
If groupId has only one section (no dots), the first
package name with classes is returned.
For example, if the group Id is
commons-logging:commons-logging, the bundle's symbolic name is
org.apache.commons.logging.
If artifactId is equal to the last section of
groupId, then groupId is
used.
For example, if the POM specifies the group ID and artifact ID as
org.apache.maven:maven, the bundle's symbolic name is
org.apache.maven.
If artifactId starts with the last section of
groupId, that portion is removed.
For example, if the POM specifies the group ID and artifact ID as
org.apache.maven:maven-core, the bundle's symbolic name is
org.apache.maven.core.
To specify your own value for the bundle's symbolic name, add a Bundle-SymbolicName child in the plug-in's instructions element, as shown in Example 2.2.
Example 2.2. Setting a bundle's symbolic name
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
...
</instructions>
</configuration>
</plugin>By default, a bundle's name is set to ${project.name}.
To
specify your own value for the bundle's name, add a Bundle-Name
child to the plug-in's instructions element, as shown in Example 2.3.
Example 2.3. Setting a bundle's name
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Name>JoeFred</Bundle-Name>
...
</instructions>
</configuration>
</plugin>By default, a bundle's version is set to ${project.version}. Any
dashes (-) are replaced with dots (.) and the number
is padded up to four digits. For example, 4.2-SNAPSHOT becomes
4.2.0.SNAPSHOT.
To specify your own value for the bundle's
version, add a Bundle-Version child to the plug-in's instructions element, as shown in Example 2.4.
Example 2.4. Setting a bundle's version
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Version>1.0.3.1</Bundle-Version>
...
</instructions>
</configuration>
</plugin>By default, the OSGi manifest's Export-Package list is populated by all of
the packages in your local Java source code (under src/main/java),
except for the deault package, ., and any packages
containing .impl or .internal.
If you use a Private-Package element in your plug-in
configuration and you do not specify a list of packages to export, the default behavior
includes only the packages listed in the Private-Package
element in the bundle. No packages are exported.
The default behavior can result in very large packages and in exporting
packages that should be kept private. To change the list of exported packages you can add
an Export-Package child to the plug-in's instructions element.
The Export-Package
element specifies a list of packages that are to be included in the bundle and that are to
be exported. The package names can be specified using the * wildcard
symbol. For example, the entry com.fuse.demo.* includes all packages on
the project's classpath that start with com.fuse.demo.
You
can specify packages to be excluded be prefixing the entry with !. For
example, the entry !com.fuse.demo.private excludes the package
com.fuse.demo.private.
When excluding packages, the order of entries in the list is important. The list is processed in order from the beginning and any subsequent contradicting entries are ignored.
For example, to include all packages starting with com.fuse.demo except the package com.fuse.demo.private, list the packages using:
!com.fuse.demo.private,com.fuse.demo.*
However, if you list the packages using com.fuse.demo.*,!com.fuse.demo.private, then com.fuse.demo.private is included in the bundle because it matches the first pattern.
If you want to specify a list of packages to include in a bundle
without exporting them, you can add a Private-Package instruction to the bundle plug-in configuration. By default, if
you do not specify a Private-Package instruction, all packages
in your local Java source are included in the bundle.
If a package matches an entry in both the Private-Package
element and the Export-Package element, the Export-Package element takes precedence. The package is added
to the bundle and exported.
The Private-Package element works similarly to
the Export-Package element in that
you specify a list of packages to be included in the bundle. The bundle plug-in uses the
list to find all classes on the project's classpath that are to be included in the bundle.
These packages are packaged in the bundle, but not exported (unless they are also selected
by the Export-Package instruction).
Example 2.5 shows the configuration for including a private package in a bundle
Example 2.5. Including a private package in a bundle
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Private-Package>org.apache.cxf.wsdlFirst.impl</Private-Package>
...
</instructions>
</configuration>
</plugin>By default, the bundle plug-in populates the OSGi manifest's Import-Package property with a list of all the packages referred to by the
contents of the bundle.
While the default behavior is typically sufficient for most projects, you might find instances where you want to import packages that are not automatically added to the list. The default behavior can also result in unwanted packages being imported.
To specify a list of packages to be imported by the bundle, add
an Import-Package child to the plug-in's instructions element. The syntax for the package list is the same as for the Export-Package element and the Private-Package element.
When you use the Import-Package element, the plug-in does
not automatically scan the bundle's contents to determine if there are any required
imports. To ensure that the contents of the bundle are scanned, you must place an
* as the last entry in the package list.
Example 2.6 shows the configuration for specifying the packages imported by a bundle
Example 2.6. Specifying the packages imported by a bundle
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Import-Package>javax.jws,
javax.wsdl,
org.apache.cxf.bus,
org.apache.cxf.bus.spring,
org.apache.cxf.bus.resource,
org.apache.cxf.configuration.spring,
org.apache.cxf.resource,
org.springframework.beans.factory.config,
*
</Import-Package>
...
</instructions>
</configuration>
</plugin>For more information on configuring a bundle plug-in, see:
Apache Karaf provides two different approaches for deploying a single OSGi bundle: hot deployment or manual deployment. If you need to deploy a collection of related bundles, on the other hand, you would be better off deploying them together as a feature, rather than singly (see Deploying Features).
Fuse ESB kernel monitors JAR files in the
directory and hot
deploys everything in this directory. Each time a JAR file is copied to this
directory, it is installed in the runtime and also started. You can subsequently
update or delete the JARs, and the changes are handled automatically.InstallDir/deploy
For example, if you have just built the bundle,
ProjectDir/target/foo-1.0-SNAPSHOT.jar,
you can deploy this bundle by copying it to the
InstallDir/deploy directory as follows
(assuming you are working on a UNIX platform):
% cpProjectDir/target/foo-1.0-SNAPSHOT.jarInstallDir/deploy
Use the osgi:install command to install one or more bundles in the
OSGi container. This command has the following syntax:
osgi:install [-s] [--start] [--help] UrlListWhere UrlList is a whitespace-separated list of URLs
that specify the location of each bundle to deploy. The following command arguments
are supported:
-sStart the bundle after installing.
--startSame as -s.
--helpShow and explain the command syntax.
For example, to install and start the bundle,
ProjectDir/target/foo-1.0-SNAPSHOT.jar,
enter the following command at the Karaf console prompt:
osgi:install -s file:ProjectDir/target/foo-1.0-SNAPSHOT.jarOn Windows platforms, you must be careful to use the correct syntax for the
file URL in this command. See File URL Handler for details.
To uninstall a bundle, you must first obtain its bundle ID using the
osgi:list command. You can then uninstall the bundle using the
osgi:uninstall command (which takes the bundle ID as its
argument).
For example, if you have already installed the bundle named named A Camel
OSGi Service Unit, entering osgi:list at the console prompt
might produce output like the following:
... [ 175] [Active ] [ ] [Started] [ 60] ServiceMix :: FTP (2009.02.0.psc-01-00RC1) [ 181] [Resolved ] [ ] [ ] [ 60] A Camel OSGi Service Unit (1.0.0.SNAPSHOT)
You can now uninstall the bundle with the ID, 181, by entering the
following console command:
osgi:uninstall 181
When specifying the location URL to the osgi:install command, you can
use any of the URL schemes supported by Fuse ESB, which includes the following scheme
types:
Applications in an OSGi environment are subject to the lifecycle of its bundles. Bundles have six lifecycle states:
Installed — All bundles start in the installed state. Bundles in the installed state are waiting for all of their dependencies to be resolved, and once they are resolved, bundles move to the resolved state.
Resolved — Bundles are moved to the resolved state when the following conditions are met:
The runtime environment meets or exceeds the environment specified by the bundle.
All of the packages imported by the bundle are exposed by bundles that are either in the resolved state or that can be moved into the resolved state at the same time as the current bundle.
All of the required bundles are either in the resolved state or they can be resolved at the same time as the current bundle.
All of an application's bundles must be in the resolved state before the application can be started.
If any of the above conditions ceases to be satisfied, the bundle is moved back into the installed state. For example, this can happen when a bundle that contains an imported package is removed from the container.
Starting — The starting state is a
transitory state between the resolved state and the active state. When a
bundle is started, the container must create the resources for the bundle.
The container also calls the start() method of the
bundle's bundle activator when one is provided.
Active — Bundles in the active state are available to do work. What a bundle does in the active state depends on the contents of the bundle. For example, a bundle containing a JAX-WS service provider indicates that the service is available to accept requests.
Stopping — The stopping state is a
transitory state between the active state and the resolved state. When a
bundle is stopped, the container must clean up the resources for the bundle.
The container also calls the stop() method of the
bundle's bundle activator when one is provided.
Uninstalled — When a bundle is uninstalled it is moved from the resolved state to the uninstalled state. A bundle in this state cannot be transitioned back into the resolved state or any other state. It must be explicitly re-installed.
The most important lifecycle states for application developers are the starting state and the stopping state. The endpoints exposed by an application are published during the starting state. The published endpoints are stopped during the stopping state.
When you install a bundle using the osgi:install command (without the
-s flag), the kernel installs the specified bundle and attempts to
put it into the resolved state. If the resolution of the bundle fails for some
reason (for example, if one of its dependencies is unsatisfied), the kernel leaves
the bundle in the installed state.
At a later time (for example, after you have installed missing dependencies) you
can attempt to move the bundle into the resolved state by invoking the
osgi:resolve command, as follows:
osgi:resolve 181
Where the argument (181, in this example) is the ID of the bundle you
want to resolve.
You can start one or more bundles (from either the installed or the resolved
state) using the osgi:start command. For example, to start the bundles
with IDs, 181, 185, and 186, enter the following console command:
osgi:start 181 185 186
You can stop one or more bundles using the osgi:stop command. For
example, to stop the bundles with IDs, 181, 185, and 186, enter the following
console command:
osgi:stop 181 185 186
You can restart one or more bundles (that is, moving from the started state to the
resolved state, and then back again to the started state) using the
osgi:restart command. For example, to restart the bundles with IDs,
181, 185, and 186, enter the following console command:
osgi:restart 181 185 186
A start level is associated with every bundle. The start
level is a positive integer value that controls the order in which bundles are
activated/started. Bundles with a low start level are started before bundles with a
high start level. Hence, bundles with the start level, 1, are started
first and bundles belonging to the kernel tend to have lower start levels, because
they provide the prerequisites for running most other bundles.
Typically, the start level of user bundles is 60 or higher.
Use the osgi:bundle-level command to set the start level of a
particular bundle. For example, to configure the bundle with ID, 181,
to have a start level of 70, enter the following console
command:
osgi:bundle-level 181 70
The OSGi container itself has a start level associated with it and this system start level determines which bundles can be active and which cannot: only those bundles whose start level is less than or equal to the system start level can be active.
To discover the current system start level, enter osgi:start-level in
the console, as follows:
karaf@root> osgi:start-level Level 100
If you want to change the system start level, provide the new start level as an
argument to the osgi:start-level command, as follows:
osgi:start-level 200
Because applications and other tools typically consist of multiple OSGi bundles, it is often convenient to aggregate inter-dependent or related bundles into a larger unit of deployment. Fuse ESB therefore provides a scalable unit of deployment, the feature, which enables you to deploy multiple bundles (and, optionally, dependencies on other features) in a single step.
Essentially, a feature is created by adding a new feature element to
a special kind of XML file, known as a feature repository. To
create a feature, perform the following steps:
If you have not already defined a custom feature repository, you can create one as
follows. Choose a convenient location for the feature repository on your file
system—for example, C:\Projects\features.xml—and use your
favorite text editor to add the following lines to it:
<?xml version="1.0" encoding="UTF-8"?>
<features name="CustomRepository">
</features>Where you can optionally specify a name for the repository,
CustomRepository, by setting the name
attribute. The default repository name is repo-0.
In contrast to a Maven repository or an OBR, a feature repository does not provide a storage location for bundles. A feature repository merely stores an aggregate of references to bundles. The bundles themselves are stored elsewhere (for example, in the file system or in a Maven repository).
To add a feature to the custom feature repository, insert a new
feature element as a child of the root features
element. You must give the feature a name and you can list any number of bundles
belonging to the feature, by inserting bundle child elements. For
example, to add a feature named example-camel-bundle containing the
single bundle,
C:\Projects\camel-bundle\target\camel-bundle-1.0-SNAPSHOT.jar, add
a feature element as follows:
<?xml version="1.0" encoding="UTF-8"?>
<features>
<feature name="example-camel-bundle">
<bundle>file:C:/Projects/camel-bundle/target/camel-bundle-1.0-SNAPSHOT.jar</bundle>
</feature>
</features>The contents of the bundle element can be any valid URL, giving the
location of a bundle (see Appendix A). You can optionally
specify a version attribute on the feature element, to assign a
non-zero version to the feature (you can then specify the version as an optional
argument to the features:install command).
To check whether the features service successfully parses the new feature entry, enter the following pair of console commands:
karaf@root> features:refreshUrl karaf@root> features:list ... [uninstalled] [0.0.0 ] example-camel-bundle repo-0 ...
The features:list command typically produces a rather long listing of
features, but you should be able to find the entry for your new feature (in this
case, example-camel-bundle) by scrolling back through the listing. The
features:refreshUrl command forces the kernel to reread all the
feature repositories: if you did not issue this command, the kernel would not be
aware of any recent changes that you made to any of the repositories (in particular,
the new feature would not appear in the listing).
To avoid scrolling through the long list of features, you can grep
for the example-camel-bundle feature as follows:
karaf@root> features:list | grep example-camel-bundle [uninstalled] [0.0.0 ] example-camel-bundle repo-0
Where the grep command (a standard UNIX pattern matching utility) is
built into the shell, so this command also works on Windows platforms.
In order to make the new feature repository available to Apache Karaf, you must add
the feature repository using the features:addUrl console command. For
example, to make the contents of the repository,
C:\Projects\features.xml, available to the kernel, you would enter
the following console command:
features:addUrl file:C:/Projects/features.xml
Where the argument to features:addUrl can be specified using any of
the supported URL formats (see Appendix A).
You can check that the repository's URL is registered correctly by entering the
features:listUrl console command, to get a complete listing of all
registered feature repository URLs, as follows:
karaf@root> features:listUrl mvn:org.apache.servicemix.nmr/apache-servicemix-nmr/1.1.0-fuse-01-00/xml/features mvn:org.apache.servicemix.camel/features/4.4.1-fuse-00-08/xml/features file:C:/Projects/features.xml mvn:org.apache.ode/ode-jbi-karaf/1.3.3-fuse-01-00/xml/features mvn:org.apache.felix.karaf/apache-felix-karaf/1.2.0-fuse-01-00/xml/features mvn:org.apache.servicemix/apache-servicemix/4.4.1-fuse-00-08/xml/features
If your feature depends on other features, you can specify these dependencies by
adding feature elements as children of the original
feature element. Each child feature element contains
the name of a feature on which the current feature depends. When you deploy a
feature with dependent features, the dependency mechanism checks whether or not the
dependent features are installed in the container. If not, the dependency mechanism
automatically installs the missing dependencies (and any recursive
dependencies).
For example, for the custom Fuse Mediation Router feature, example-camel-bundle,
you can specify explicitly which standard Fuse Mediation Router features it depends on. This has
the advantage that the application could now be successfully deployed and run, even
if the OSGi container does not have the required features pre-deployed. For example,
you can define the example-camel-bundle feature with Apache Camel
dependencies as follows:
<?xml version="1.0" encoding="UTF-8"?>
<features>
<feature name="example-camel-bundle">
<bundle>file:C:/Projects/camel-bundle/target/camel-bundle-1.0-SNAPSHOT.jar</bundle>
<feature version="4.4.1-fuse-00-08">camel-core</feature>
<feature version="4.4.1-fuse-00-08">camel-spring-osgi</feature>
<feature version="4.4.1-fuse-00-08">servicemix-camel</feature>
</feature>
</features>Specifying the version attribute is optional. When present, it
enables you to select the specified version of the feature.
If your application uses the OSGi Configuration Admin
service, you can specify configuration settings for this service using the
config child element of your feature definition. For example, to
specify that the prefix property has the value,
MyTransform, add the following config child element to
your feature's configuration:
<?xml version="1.0" encoding="UTF-8"?>
<features>
<feature name="example-camel-bundle">
<config name="org.fusesource.fuseesb.example">
prefix=MyTransform
</config>
</feature>
</features>Where the name attribute of the config element specifies
the persistent ID of the property settings (where the
persistent ID acts effectively as a name scope for the property names). The content
of the config element is parsed in the same way as a Java properties file.
The settings in the config element can optionally be overriden by the
settings in the Java properties file located in the
directory, which is
named after the persistent ID, as follows:InstallDir/etc
InstallDir/etc/org.fusesource.fuseesb.example.cfgAs an example of how the preceding configuration properties can be used in practice, consider the following Spring XML file that accesses the OSGi configuration properties using Spring DM:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ctx="http://www.springframework.org/schema/context"
xmlns:osgi="http://camel.apache.org/schema/osgi"
xmlns:osgix="http://www.springframework.org/schema/osgi-compendium" ...>
...
<bean id="myTransform" class="org.fusesource.fuseesb.example.MyTransform">
<property name="prefix" value="${prefix}"/>
</bean>
<osgix:cm-properties id="preProps" persistent-id="org.fusesource.fuseesb.example">
<prop key="prefix">DefaultValue</prop>
</osgix:cm-properties>
<ctx:property-placeholder properties-ref="preProps" />
</beans>When this Spring XML file is deployed in the example-camel-bundle
bundle, the property reference, ${prefix}, is replaced by the value,
MyTransform, which is specified by the config element
in the feature repository.
You can deploy a feature in one of the following ways:
Install at the console, using features:install.
Use hot deployment.
Modify the boot configuration (first boot only!).
After you have created a feature (by adding an entry for it in a feature
repository and registering the feature repository), it is relatively easy to deploy
the feature using the features:install console command. For example, to
deploy the example-camel-bundle feature, enter the following pair of
console commands:
karaf@root> features:refreshUrl karaf@root> features:install example-camel-bundle
It is recommended that you invoke the features:refreshUrl command
before calling features:install, in case any recent changes were made
to the features in the feature repository which the kernel has not picked up yet.
The features:install command takes the feature name as its argument
(and, optionally, the feature version as its second argument).
Features use a flat namespace. So when naming your features, be careful to avoid name clashes with existing features.
To uninstall a feature, invoke the features:uninstall command as
follows:
karaf@root> features:uninstall example-camel-bundle
After uninstalling, the feature will still be visible when you invoke
features:list, but its status will now be flagged as
[uninstalled].
You can hot deploy all of the features in a feature
repository simply by copying the feature repository file into the
directory.InstallDir/deploy
As it is unlikely that you would want to hot deploy an entire feature repository at once, it is often more convenient to define a reduced feature repository or feature descriptor, which references only those features you want to deploy. The feature descriptor has exactly the same syntax as a feature repository, but it is written in a different style. The difference is that a feature descriptor consists only of references to existing features from a feature repository.
For example, you could define a feature descriptor to load the
example-camel-bundle feature as follows:
<?xml version="1.0" encoding="UTF-8"?> <features name="CustomDescriptor"> <repository>RepositoryURL</repository> <feature name="hot-example-camel-bundle"> <feature>example-camel-bundle</feature> </feature> </features>
The repository element specifies the location of the custom feature repository,
RepositoryURL (where you can use any of the URL
formats described in Appendix A). The feature,
hot-example-camel-bundle, is just a reference to the existing
feature, example-camel-bundle.
If you want to provision copies of the Apache Karaf for deployment on multiple hosts, you might be interested in adding a feature to the boot configuration, which determines the collection of features that are installed when Apache Karaf boots up for the very first time.
The configuration file, /etc/org.apache.felix.karaf.features.cfg, in
your install directory contains the following settings:
... # # Comma separated list of features repositories to register by default # featuresRepositories=mvn:org.apache.felix.karaf/apache-felix-karaf/1.1.0.3-fuse-SNAPSHOT/xml/features, mvn:org.apache.servicemix.nmr/apache-servicemix-nmr/1.1.0-fuse-SNAPSHOT/xml/features, mvn:org.apache.servicemix/apache-servicemix/4.2.0-fuse-SNAPSHOT/xml/features, mvn:org.apache.camel.karaf/apache-camel/2.x-fuse-SNAPSHOT/xml/features, mvn:org.apache.ode/ode-jbi-karaf/1.3.3-fuse-SNAPSHOT/xml/features # # Comma separated list of features to install at startup # # Will put these back in when we decide to include these components # servicemix-smpp,servicemix-snmp,servicemix-vfs, featuresBoot=activemq,activemq-broker,camel,jbi-cluster,web,servicemix-cxf-bc,servicemix-file, servicemix-ftp,servicemix-http,servicemix-jms,servicemix-mail,servicemix-bean,servicemix-camel, servicemix-cxf-se,servicemix-drools,servicemix-eip,servicemix-osworkflow,servicemix-quartz, servicemix-scripting,servicemix-validation,servicemix-saxon,servicemix-wsn2005,camel-cxf,camel-jms
This configuration file has two properties:
featuresRepositories—Comma separated list of feature
repositories to load at startup.
featuresBoot—Comma separated list of features to
install at startup.
You can modify the configuration to customize the features that are installed as Fuse ESB kernel starts up. You can also modify this configuration file, if you plan to distribute Fuse ESB kernel with pre-installed features.
This method of adding a feature is only effective the first
time a particular Apache Karaf instance boots up. Any changes made
subsequently to the featuresRepositories setting and the
featuresBoot setting are ignored, even if you restart the
Apache Karaf.
You could force the Apache Karaf instance to revert back to its initial state,
however, by deleting the complete contents of the
InstallDir/data/cache (thereby losing all of the Apache Karaf
instance's custom settings).
This chapter explains how to deal with JAR libraries that are not already packaged as bundles. If a JAR exposes a public API, typically the best solution is to convert the existing JAR library into a bundle, enabling the JAR library to be shared with other bundles. This chapter describes how to perform the conversion process automatically, using the open source Bnd tool.
The Bnd tool is a an open source utility for creating and diagnosing OSGi bundles. It has been developed by Peter Kriens and is freely downloadable from the aQute Web site (subject to an Apache version 2.0 open source license). The key feature of the Bnd tool is that it automatically generates Manifest headers for the OSGi bundle, thus relieving you of this tedious task. The main tasks that Bnd can perform are:
Print the manifiest and show the package dependencies of a JAR file or bundle file.
Wrap a vanilla JAR file, converting it into a bundle.
Build a bundle from the class path, based on specifications in a
.bnd file.
Validate manifest entries.
You have the option of invoking Bnd in any of the following ways: from the command
line; as an Ant task; or through the Maven bundle plug-in,
maven-bundle-plugin. In fact, the approach to building bundles
described in Building Bundles with Maven is based on the Maven bundle plug-in and
therefore, implicitly, is also based on the Bnd tool.
You can download Bnd from the aQute Web site at the following location:
http://www.aqute.biz/Code/Download#bnd
Download the Bnd JAR file,
bnd-, to a convenient location.
There is no installation involved: the JAR file is all that you need to use the Bnd
tool. For convenience, however, it is advisable to rename the JAR file to
Version.jarbnd.jar, so you won't have to do so much typing when you invoke it
from the command line (for example, as in java -jar bnd.jar
)Cmd
Options
To learn more about the Bnd tool, consult the following references:
This section describes how to convert a vanilla JAR file into an OSGi bundle using
the Bnd tool's wrap command. You can choose either to perform the
conversion with default settings (which works in most cases) or to perform a custom
conversion with the help of a Bnd properties file.
To demonstrate how to convert a plain JAR into a bundle, we will consider the example of the commons-logging-Version.jar, which is available from the Apache Commons project and can be downloaded from the following location:
http://commons.apache.org/downloads/download_logging.cgi
Actually, this is a rather artificial example, because the Apache Commons logging API is not intended to be deployed as an OSGi bundle (which is why it does not have the requisite Manifest headers in the first place). Most of the other JARs from Apache Commons are already provided as bundles.
The Bnd print command is a useful diagnostic tool that displays most
of the information about a JAR that is relevant to bundle creation. For example, to
print the Manifest headers and package dependencies of the commons logging JAR, you
can invoke the Bnd print command as follows:
java -jar bnd.jar print commons-logging-1.1.1.jar
The preceding command produces the following output:
[MANIFEST commons-logging-1.1.1.jar]
Archiver-Version Plexus Archiver
Build-Jdk 1.4.2_16
Built-By dlg01
Created-By Apache Maven
Extension-Name org.apache.commons.logging
Implementation-Title Commons Logging
Implementation-Vendor Apache Software Foundation
Implementation-Vendor-Id org.apache
Implementation-Version 1.1.1
Manifest-Version 1.0
Specification-Title Commons Logging
Specification-Vendor Apache Software Foundation
Specification-Version 1.0
X-Compile-Source-JDK 1.2
X-Compile-Target-JDK 1.1
[IMPEXP]
[USES]
org.apache.commons.logging org.apache.commons.logging.impl
org.apache.commons.logging.impl javax.servlet
org.apache.avalon.framework.logger
org.apache.commons.logging
org.apache.log
org.apache.log4j
One error
1 : Unresolved references to [javax.servlet, org.apache.avalon.framework.logger,
org.apache.log, org.apache.log4j] by class(es) on the Bundle-Classpath[Jar:comm
ons-logging-1.1.1.jar]: [org/apache/commons/logging/impl/AvalonLogger.class, org
/apache/commons/logging/impl/ServletContextCleaner.class, org/apache/commons/log
ging/impl/LogKitLogger.class, org/apache/commons/logging/impl/Log4JLogger.class]From this output, you can see that the JAR does not define any bundle manifest headers. The output consists of the following sections:
[MANIFEST JarFileName]Lists all of the header settings from the JAR file's
META-INF/Manifest.mf file.
[IMPEXP]Lists any Java packages that are imported or exported through the
Import-Package or Export-Package Manifest
headers.
[USES]Shows the JAR's package dependencies. The left column lists all of the packages defined in the JAR, while the right column lists the dependent packages for each of the packages in the left column.
Lists any errors—for example, any unresolved dependencies.
To convert the plain commons logging JAR into an OSGi bundle, invoke the Bnd
wrap command as follows:
java -jar bnd.jar wrap commons-logging-1.1.1.jar
The result of running this command is a bundle file,
commons-logging-1.1.1.bar, which consists of the original JAR
augmented by the Manifest headers required by a bundle.
To display the Manifest headers and package dependencies of the newly created
bundle JAR, enter the following Bnd print command:
java -jar bnd.jar print commons-logging-1.1.1.bar
The preceding command should produce output like the following:
[MANIFEST commons-logging-1.1.1-bnd.jar]
Archiver-Version Plexus Archiver
Bnd-LastModified 1263987809524
Build-Jdk 1.4.2_16
Built-By dlg01
Bundle-ManifestVersion 2
Bundle-Name commons-logging-1.1.1
Bundle-SymbolicName commons-logging-1.1.1
Bundle-Version 0
Created-By 1.5.0_08 (Sun Microsystems Inc.)
Export-Package org.apache.commons.logging;uses:="org.ap
ache.commons.logging.impl",org.apache.commons.logging.impl;uses:="org.apache.ava
lon.framework.logger,org.apache.commons.logging,org.apache.log4j,org.apache.log,
javax.servlet"
Extension-Name org.apache.commons.logging
Implementation-Title Commons Logging
Implementation-Vendor Apache Software Foundation
Implementation-Vendor-Id org.apache
Implementation-Version 1.1.1
Import-Package javax.servlet;resolution:=optional,org.a
pache.avalon.framework.logger;resolution:=optional,org.apache.commons.logging;re
solution:=optional,org.apache.commons.logging.impl;resolution:=optional,org.apac
he.log;resolution:=optional,org.apache.log4j;resolution:=optional
Manifest-Version 1.0
Originally-Created-By Apache Maven
Specification-Title Commons Logging
Specification-Vendor Apache Software Foundation
Specification-Version 1.0
Tool Bnd-0.0.384
X-Compile-Source-JDK 1.2
X-Compile-Target-JDK 1.1
[IMPEXP]
Import-Package
javax.servlet {resolution:=optional}
org.apache.avalon.framework.logger {resolution:=optional}
org.apache.log {resolution:=optional}
org.apache.log4j {resolution:=optional}
Export-Package
org.apache.commons.logging
org.apache.commons.logging.impl
[USES]
org.apache.commons.logging org.apache.commons.logging.impl
org.apache.commons.logging.impl javax.servlet
org.apache.avalon.framework.logger
org.apache.commons.logging
org.apache.log
org.apache.log4jBy default, the Bnd wrap command behaves as if it was configured to
use the following Bnd property file:
Export-Package: *
Import-Package: AllReferencedPackagesThe result of this configuration is that the new bundle imports all of the packages referenced by the JAR (which is almost always what you need) and all of the packages defined in the JAR are exported. Sometimes you might want to hide some of the packages in the JAR, however, in which case you would need to define a custom property file.
If you want to have more control over the way the Bnd wrap command
generates a bundle, you can define a Bnd properties file to control the conversion
process. For a detailed description of the syntax and capabilities of the Bnd
properties file, see the Bnd tool
documentation.
For example, in the case of the commons logging JAR, you might decide to hide the
org.apache.commons.logging.impl package, while exporting the
org.apache.commons.logging package. You could do this by creating a
Bnd properties file called commons-logging-1.1.1.bnd and inserting the
following lines using a text editor:
version=1.1.1
Export-Package: org.apache.commons.logging;version=${version}
Private-Package: org.apache.commons.logging.impl
Bundle-Version: ${version}Notice how a version number is assigned to the exported package by substituting
the version variable (any properties starting with a lowercase letter
are interpreted as variables).
To wrap a JAR file using the custom property file, specify the Bnd properties file
using the -properties option of the wrap command. For
example, to wrap the vanilla commons logging JAR using the instructions contained in
the commons-logging-1.1.1.bnd properties file, enter the following
command:
java -jar bnd.jar wrap -properties commons-logging-1.1.1.bnd commons-logging-1.1.1.jar
You also have the option of converting a JAR into a bundle using the
wrap scheme, which can be prefixed to any existing URL format. The
wrap scheme is also based on the Bnd utility.
The wrap scheme has the following basic syntax:
wrap:LocationURLThe wrap scheme can prefix any URL that locates a JAR. The locating
part of the URL, LocationURL, is used to obtain the
(non-bundlized) JAR and the URL handler for the wrap scheme then
converts the JAR automatically into a bundle.
The wrap scheme also supports a more elaborate syntax, which
enables you to customize the conversion by specifying a Bnd properties file or
by specifying individual Bnd properties in the URL. Typically, however, the
wrap scheme is used just with its default settings.
Because the wrap scheme is based on the Bnd utility, it uses exactly
the same default properties to generate the bundle as Bnd does—see Default property file.
The following example shows how you can use a single console command to download
the plain commons-logging JAR from a remote Maven repository, convert
it into an OSGi bundle on the fly, and then install it and start it in the OSGi
container:
karaf@root> osgi:install -s wrap:mvn:commons-logging/commons-logging/1.1.1
Example 5.1 shows how the example-jpa-osgi
feature combines the mvn scheme and the wrap scheme in
order to download the plain HyperSQL JAR file and convert it to an OSGi bundle on
the fly.
Example 5.1. The example-jpa-osgi Feature
<feature name="examples-jpa-osgi" version="4.4.1-fuse-00-08">
<feature version="4.4.1-fuse-00-08">jpa-hibernate</feature>
<bundle>wrap:mvn:hsqldb/hsqldb/1.8.0.7</bundle>
<bundle>mvn:org.apache.servicemix.examples.jpa-osgi/wsdl-first-cxfbc-bundle/4.4.1-fuse-00-08</bundle>
<bundle>mvn:org.apache.servicemix.examples.jpa-osgi/wsdl-first-cxfse-bundle/4.4.1-fuse-00-08</bundle>
</feature>The wrap scheme is provided by the Pax project, which is
the umbrella project for a variety of open source OSGi utilities. For full
documentation on the wrap scheme, see the Wrap
Protocol reference page.
This chapter explains how to deploy a Web archive (WAR) file as a bundle in the
OSGi container. Conversion to a bundle is performed automatically by the PAX War
URL, which is based on the open source Bnd tool. The presence of a
web.xml file in the bundle signals to the container that the bundle
should be deployed as a Web application.
To convert a WAR file into a bundle suitable for deployment in the OSGi container,
add the war: prefix to the WAR URL. The PAX War URL handler acts as a wrapper, which
adds the requisite manifest headers to the WAR file.
The war scheme has the following basic syntax:
war:LocationURL[?Options]
The location URL, LocationURL, can be any of the
location URLs described in Appendix A (for example, an
mvn: or a file: URL). Options can be appended to the
URL in the following format:
?Option=Value&Option=Value&...
Or if the war URL appears in an XML file:
?Option=Value&Option=Value&...
If the WAR file is stored in a Maven repository, you can deploy it into the OSGi
container using the osgi:install command, taking a
war:mvn: URL as its argument. For example, to deploy the
wicket-example WAR file from a Maven repository, where the application should be
accessible from the wicket Web application context, enter the following
console command:
karaf@root> install war:mvn:org.apache.wicket/wicket-examples/1.4.7/war?Web-ContextPath=wicket
Alternatively, if the WAR file is stored on the filesystem, you can deploy it into
the OSGi container by specifying a war:file: URL. For example, to
deploy the WAR file, wicket-example-1.4.6.war, enter the following
console command:
karaf@root> install war:file://wicket-examples-1.4.7.war?Web-ContextPath=wicket
The WAR file is automatically installed into a Web container, which listens on the
IP port 8181 by default, and the Web container uses the Web application context
specified by the Web-ContextPath option. For example, the
wicket-example WAR deployed in the preceding examples, would be
accessible from the following URL:
http://localhost:8181/wicket
The PAX War URL handler converts a WAR file to a special kind of OSGi bundle,
which includes additional Manifest headers to support WAR deployment (for example,
the Web-ContextPath Manifest header). By default, the deployed WAR is
configured as an isolated bundle (neither importing nor exporting any packages).
This mimics the deployment model of a WAR inside a J2EE container, where the WAR is
completely self-contained, including all of the JAR files it needs.
For details of the default conversion parameters, see Table A.2.
The PAX War URL handler is layered over Bnd. If you want to customize the bundle headers in the Manifest file, you can either add a Bnd instruction as a URL option or you can specify a Bnd instructions file for the War URL handler to use—for details, see War URL Handler.
In particular, you might sometimes find it necessary to customize the entry for
the Bundle-ClassPath, because the default value of
Bundle-ClassPath does not include all of the
resources in the WAR file (see Table A.2).
Support for running WARs in the OSGi container is provided by the PAX WAR
Extender, which monitors each bundle as it starts and, if the bundle
contains a web.xml file, automatically deploys the WAR in a Web
container. The War
Protocol page has the original reference documentation for the War URL
handler.
Fuse ESB automatically deploys WAR files into a Web container, which is implemented by the PAX Web library. You can configure the Web container through the OSGi Configuration Admin service.
The Web container uses the following configuration file:
EsbInstallDir/etc/org.ops4j.pax.web.cfg You must create this file, if it does not already exist in the
directory.EsbInstallDir/etc/
By default, the Web container listens on the IP port, 8181. You can change this
value by editing the etc/org.ops4j.pax.web.cfg file and setting the
value of the org.osgi.service.http.port property, as follows:
# Configure the Web container org.osgi.service.http.port=8181
The Web container is also used for deploying the Fuse ESB Web console. The instructions for securing the Web container with SSL/TLS are identical to the instructions for securing the Web console with SSL/TLS. See ???? for details.
This chapter explains how to package Fuse Mediation Router applications for deployment into the OSGi container.
If you want to deploy your routes in a blueprint configuration file, you must
first install the camel-blueprint feature (it is not installed by
default). To install the camel-blueprint feature, enter the following
console command:
karaf@root> features:install camel-blueprint
If you have an existing Fuse Mediation Router XML configuration file, you can deploy the routes by copying the configuration file into the following hot deploy directory:
InstallDir/deployAfter deploying, the configured routes start up immediately.
For a detailed example of hot deployed routes, see JMS Endpoints in a Router Application.
You can hot deploy Fuse Mediation Router routes using either of the following types of configuration file:
You can deploy a camelContext using a Spring configuration file,
where the root element is a Spring beans element and the
camelContext element is a child of the beans element.
In this case, the camelContext namespace must be
http://camel.apache.org/schema/spring.
For example, the following Spring configuration defines a route that generates
timer messages every two seconds, sending the messages to the
ExampleRouter log (which get incorporated into the console log
file,
):InstallDir/data/log/servicemix.log
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="timer://myTimer?fixedRate=true&period=2000"/> <to uri="log:ExampleRouter"/> </route> </camelContext> </beans>
It is not necessary to specify schema locations in the configuration. But if you are editing the configuration file with an XML editor, you might want to add the schema locations in order to support schema validation and content completion in the editor. For the preceding example, you could specify the schema locations as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
...Before deploying routes in a blueprint configuration file, check that the camel-blueprint feature is already installed.
You can deploy a camelContext using a blueprint configuration file,
where the root element is blueprint and the camelContext
element is a child of the blueprint element. In this case, the
camelContext namespace must be
http://camel.apache.org/schema/blueprint.
For example, the following blueprint configuration defines a route that generates
timer messages every two seconds, sending the messages to the
ExampleRouter log (which get incorporated into the console log
file,
):InstallDir/data/log/servicemix.log
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <route> <from uri="timer://myTimer?fixedRate=true&period=2000"/> <to uri="log:ExampleRouter"/> </route> </camelContext> </blueprint>
Blueprint is a dependency injection framework, defined by the OSGi standard, which is similar to Spring in many respects. For more details about blueprint, see The Blueprint Container.
This section explains how to modify an existing Maven project for a Fuse Mediation Router application, so that the project generates an OSGi bundle suitable for deployment in the Fuse ESB OSGi container. To convert the Maven project, you need to modify the project POM file.
To configure a Maven POM file to generate a bundle, there are essentially two
changes you need to make: change the POM's package type to bundle; and
add the Maven bundle plug-in to your POM. For details, see Modifying an Existing Maven Project.
There are two kinds of file that you can use to configure your project:
Spring configuration—in the standard Maven
directory layout, Spring XML configuration files are located under
.ProjectDir/src/main/resources/META-INF/spring
Blueprint configuration—in the standard Maven
directory layout, blueprint XML configuration files are located under
.ProjectDir/src/main/resources/OSGI-INF/blueprint
If you decide to use the blueprint configuration, you can embed
camelContext elements in the blueprint file, as described in Blueprint configuration file.
If you decide to configure your Fuse Mediation Router application using blueprint, you must
ensure that the camel-blueprint feature is installed (it is not
installed by default). If necessary, install it by entering the following console
command:
karaf@root> features:install camel-blueprint
The OSGi Configuration Admin service defines a mechanism for passing configuration settings to an OSGi bundle. You do not have to use this service for configuration, but it is typically the most convenient way of configuring bundle applications. Spring DM provides support for OSGi configuration, enabling you to substitute variables in a Spring XML file using values obtained from the OSGi Configuration Admin service.
Example 7.1 shows how to pass the value
of the prefix variable to the constructor of the
MyTransform bean, where the value of prefix can be set
by the OSGi Configuration Admin service:
Example 7.1. Using OSGi Configuration Properties in Spring XML
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ctx="http://www.springframework.org/schema/context"
xmlns:osgi="http://camel.apache.org/schema/osgi"
xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
... >
...
<bean id="myTransform" class="org.fusesource.example.MyTransform">
<property name="prefix" value="${prefix}"/>
</bean>
<osgix:cm-properties id="preProps" persistent-id="org.fusesource.example">
<prop key="prefix">MyTransform</prop>
</osgix:cm-properties>
<ctx:property-placeholder properties-ref="preProps" />
</beans>The syntax, ${prefix}, substitutes the value of the
prefix variable into the Spring XML file. The OSGi properties are
set up using the following XML elements:
osgix:cm-propertiesTo integrate Spring properties with the properties from the OSGi
Configuration Admin service, insert an osgix:cm-properties
element into the Spring XML file. This element creates a bean that gets
injected with all of the properties from the OSGi
ManagedService instance that is identified by the
persistent-id attribute. The minimal configuration
consists of an empty osgix:cm-properties element that sets
the persistent-id attribute and the id
attribute—for example:
<osgix:cm-properties id="preProps" persistent-id="org.fusesource.example"/>
For an example of how the persistent ID relates to OSGi configuration settings, see the example in Add OSGi configurations to the feature.
If you want to define defaults for some of the properties in the
Spring XML file, add prop elements as children of the
osgix:cm-properties element, as shown in Example 7.1.
ctx:property-placeholderProperty placeholder is a Spring mechanism that enables you
to use the syntax, ${,
to substitute variables in a Spring XML file. By defining a
PropName}ctx:property-placeholder element with a reference to
the preProps bean (as in Example 7.1), you enable the
property placeholder mechanism to substitute any of the variables from
the preProps bean (which encapsulates the OSGi
configuration properties) into the Spring XML file.
This section explains how to generate, build, and run a complete Fuse Mediation Router example in the OSGi container, where the starting point code is generated with the help of a Maven archetype.
In order to generate a project using an Fuse ESB Maven archetype, you must have the following prerequisites:
Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
fusesource Maven repository is configured—in
order to locate the archetypes, Maven's settings.xml file must
be configured with the location of the fusesource Maven
repository. For details of how to set this up, see Adding the fusesource repository.
The servicemix-camel-osgi-bundle archetype creates a project for
building a route that can be deployed into the OSGi container. To generate a Maven
project with the coordinates, org.fusesource.example:camel-bundle,
enter the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-camel-osgi-bundle -DarchetypeVersion=2011.02.1-fuse-00-08 -DgroupId=org.fusesource.example -DartifactId=camel-bundle
The result of this command is a directory,
, containing
the files for the generated bundle project.ProjectDir/camel-bundle
To install and run the generated camel-bundle project, perform the
following steps:
Build the project—open a command prompt and
change directory to
. Use
Maven to build the demonstration by entering the following command:ProjectDir/camel-bundle
mvn install
If this command runs successfully, the
directory should contain the bundle file,
ProjectDir/camel-bundle/targetcamel-bundle-1.0-SNAPSHOT.jar and the bundle will also be
installed in the local Maven repository.
Install prerequisite features (optional)—by
default, the camel-core feature and some related features are
pre-installed in the OSGi container. But many of the Fuse Mediation Router components are
not installed by default. To check which features
are available and whether or not they are installed, enter the following
console command:
karaf@root> features:list
Fuse Mediation Router features are identifiable by the camel- prefix. For
example, if one of your routes requires the HTTP component, you can make
sure that it is installed in the OSGi container by issuing the following
console command:
karaf@root> features:install camel-http
Install and start the camel-bundle bundle—at the Fuse ESB console, enter the following command:
karaf@root> osgi:install -s file:ProjectDir/camel-bundle/target/camel-bundle-1.0-SNAPSHOT.jarWhere ProjectDir is the directory containing
your Maven projects and the -s flag directs the container to
start the bundle right away. For example, if your project directory is
C:\Projects on a Windows machine, you would enter the
following command:
karaf@root> osgi:install -s file:C:/Projects/camel-bundle/target/camel-bundle-1.0-SNAPSHOT.jar
Alternatively, you could install the bundle from the local Maven repository using an Mvn URL (see Mvn URL Handler) as follows:
karaf@root> osgi:install -s mvn:org.fusesource.example/camel-bundle
After entering this command, you should soon see output like the following being logged to the console screen:
>>>> MyTransform set body: Mon Sep 22 11:43:42 BST 2008 >>>> MyTransform set body: Mon Sep 22 11:43:44 BST 2008 >>>> MyTransform set body: Mon Sep 22 11:43:46 BST 2008
On Windows machines, be careful how you format the file
URL—for details of the syntax understood by the file
URL handler, see File URL Handler.
Stop the camel-bundle bundle—to stop the
camel-bundle bundle, you first need to discover the
relevant bundle number. To find the bundle number, enter the following
console command (this might look a bit confusing, because the text you are
typing will intermingle with the output that is being logged to the
screen):
karaf@root> osgi:list
At the end of the listing, you should see an entry like the following:
[ 189] [Active ] [ ] [ ] [ 60] A Camel OSGi Service Unit (1.0.0.SNAPSHOT)
Where, in this example, the bundle number is 189. To stop this bundle, enter the following console command:
karaf@root> osgi:stop 189
This chapter explains how to package Fuse Service Framework applications for deployment into the OSGi container.
This section explains how to modify an existing Maven project for a Fuse Service Framework
application, so that the project generates an OSGi bundle suitable for deployment in
the Fuse ESB OSGi container. To convert the Maven project, you need to modify the
project's POM file and the project's Spring XML file(s) (located in
META-INF/spring).
To configure a Maven POM file to generate a bundle, there are essentially two
changes you need to make: change the POM's package type to bundle; and
add the Maven bundle plug-in to your POM. For details, see Modifying an Existing Maven Project.
The Fuse Service Framework runtime components are included in Fuse ESB as an OSGi bundle called
org.apache.cxf.bundle. The dependency on this
bundle can conveniently be expressed by adding the Require-Bundle
element to the Maven bundle plug-in's instructions, as highlighted in the following
sample POM:
<project ... >
...
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
...
<Require-Bundle>org.apache.cxf.bundle</Require-Bundle>
...
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>In order for your application to use the Fuse Service Framework components, you need to import their packages into the application's bundle. Because of the complex nature of the dependencies in Fuse Service Framework, you cannot rely on the Maven bundle plug-in, or the bnd tool, to automatically determine the needed imports. You will need to explicitly declare them.
You need to import the following packages into your bundle:
javax.jws
javax.wsdl
javax.xml.bind
javax.xml.bind.annotation
javax.xml.namespace
javax.xml.ws
META-INF.cxf
META-INF.cxf.osgi
org.apache.cxf.bus
org.apache.cxf.bus.spring
org.apache.cxf.bus.resource
org.apache.cxf.configuration.spring
org.apache.cxf.resource
org.apache.cxf.jaxws
org.apache.servicemix.cxf.transport.http_osgi
org.springframework.beans.factory.config
Example 8.1 shows how to configure
the Maven bundle plug-in in your POM to import the mandatory packages. The mandatory
import packages appear as a comma-separated list inside the
Import-Package element. Note the appearance of the wildcard,
*, as the last element of the list. The wildcard ensures that the
Java source files from the current bundle are scanned to discover what additional
packages need to be imported.
Example 8.1. Configuration of Mandatory Import Packages
<project ... >
...
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
...
<Import-Package>
javax.jws,
javax.wsdl,
javax.xml.bind,
javax.xml.bind.annotation,
javax.xml.namespace,
javax.xml.ws,
META-INF.cxf,
META-INF.cxf.osgi,
org.apache.cxf.bus,
org.apache.cxf.bus.spring,
org.apache.cxf.bus.resource,
org.apache.cxf.configuration.spring,
org.apache.cxf.resource,
org.apache.cxf.jaxws,
org.apache.cxf.transport.http_osgi,
org.springframework.beans.factory.config,
*
</Import-Package>
...
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>The OSGi Configuration Admin service defines a mechanism for passing configuration settings to an OSGi bundle. You do not have to use this service for configuration, but it is typically the most convenient way of configuring bundle applications. Both Spring DM and Blueprint provide support for OSGi configuration, enabling you to substitute variables in a Spring XML file or a Blueprint file using values obtained from the OSGi Configuration Admin service.
For details of how to use OSGi configuration properties, see Use OSGi configuration properties (optional) and Add OSGi configurations to the feature.
This section explains how to generate, build, and run a complete Fuse Service Framework example in the OSGi container, where the starting point code is generated with the help of a Maven archetype.
In order to generate a project using a Fuse ESB Maven archetype, you must have the following prerequisites:
Maven installation—Maven is an open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
fusesource Maven repository is configured—in
order to locate the archetypes, Maven's settings.xml file must
be configured with the location of the fusesource Maven
repository. For details of how to set this up, see Adding the fusesource repository.
The servicemix-cxf-code-first-osgi-bundle archetype creates a project
for building a Java-first JAX-WS application that can be deployed into the OSGi
container. To generate a Maven project with the coordinates,
org.fusesource.example:cxf-code-first-bundle, enter the following
command:
mvn archetype:create -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-cxf-code-first-osgi-bundle -DarchetypeVersion=2011.02.1-fuse-00-08 -DgroupId=org.fusesource.example -DartifactId=cxf-code-first-bundle
The result of this command is a directory,
,
containing the files for the generated bundle project.ProjectDir/cxf-code-first-bundle
Typically, you will need to modify the instructions for the Maven bundle plug-in
in the POM file. In particular, the default Import-Package element
generated by the servicemix-cxf-code-first-osgi-bundle archetype is not
configured to scan the project's Java source files. In most cases, however, you
would want the Maven bundle plug-in to perform this automatic scanning in order to
ensure that the bundle imports all of the packages needed by your code.
To enable the Import-Package scanning feature, simply add the
wildcard, *, as the last item in the comma-separated list inside the
Import-Package element, as shown in the following example:
<project ... >
...
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
...
<Import-Package>
javax.jws,
javax.wsdl,
javax.xml.bind,
javax.xml.bind.annotation,
javax.xml.namespace,
javax.xml.ws,
META-INF.cxf,
META-INF.cxf.osgi,
org.apache.cxf.bus,
org.apache.cxf.bus.spring,
org.apache.cxf.bus.resource,
org.apache.cxf.configuration.spring,
org.apache.cxf.resource,
org.apache.cxf.jaxws,
org.apache.cxf.transport.http_osgi,
org.springframework.beans.factory.config,
*
</Import-Package>
...
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>To install and run the generated cxf-code-first-bundle project,
perform the following steps:
Build the project—open a command prompt and
change directory to
.
Use Maven to build the demonstration by entering the following
command:ProjectDir/cxf-code-first-bundle
mvn install
If this command runs successfully, the
directory should contain the bundle file,
ProjectDir/cxf-code-first-bundle/targetcxf-code-first-bundle-1.0-SNAPSHOT.jar.
Install and start the cxf-code-first-bundle bundle—at the Fuse ESB console, enter the following command:
karaf@root> osgi:install -s file:ProjectDir/cxf-code-first-bundle/target/cxf-code-first-bundle-1.0-SNAPSHOT.jarWhere ProjectDir is the directory containing
your Maven projects and the -s flag directs the container to
start the bundle right away. For example, if your project directory is
C:\Projects on a Windows machine, you would enter the
following command:
karaf@root> osgi:install -s file:C:/Projects/cxf-code-first-bundle/target/cxf-code-first-bundle-1.0-SNAPSHOT.jar
On Windows machines, be careful how you format the file
URL—for details of the syntax understood by the file
URL handler, see File URL Handler.
Alternatively, you could install the bundle from your local Maven
repository, using the following PAX mvn URL:
karaf@root> osgi:install -s mvn:org.fusesource.example/cxf-code-first-bundle/1.0-SNAPSHOT
Test the Web serivce—to test the Web service deployed in the previous step, you can use a web browser to query the service's WSDL. Open your favorite web browser and navigate to the following URL:
http://localhost:8181/cxf/PersonServiceCF?wsdl
When the web service receives the query, ?wsdl, it returns a
WSDL description of the running service.
Stop the cxf-code-first-bundle bundle—to stop
the cxf-code-first-bundle bundle, you first need to discover
the relevant bundle number. To find the bundle number, enter the following
console command:
karaf@root> osgi:list
At the end of the listing, you should see an entry like the following:
[ 191] [Active ] [ ] [ ] [ 60] A CXF Code First OSGi Project (1.0.0.SNAPSHOT)
Where, in this example, the bundle number is 191. To stop this bundle, enter the following console command:
karaf@root> osgi:stop 191
Fuse ESB supports the deployment of JMS brokers through the activemq
feature. After this feature is installed, you can deploy a JMS broker simply by
copying its broker configuration file to the hot deploy directory.
When you start up the OSGi container for the first time, it automatically installs and activates a default broker instance. The default broker creates an Openwire port that listens on IP port 61616 and a Stomp port that listens on IP port 61613. The broker remains installed in the OSGi container and activates whenever you restart the OSGi container.
You can examine the broker configuration by looking at the contents of the file
,
which contains a copy of the default broker's configuration. It is
not possible to change the default broker's configuration
by editing this file, however. The active copy of the default broker configuration
is already deployed as a bundle in the OSGi container.InstallDir/etc/activemq-broker.xml
The default broker is deployed as the bundle named,
activemq-broker.xml. To discover the default broker's bundle ID,
enter the following console command:
osgi:list | grep activemq
You should see some output like the following:
[ 42] [Active ] [Created ] [ ] [ 60] activemq-core (5.4.0.fuse-00-00)
[ 44] [Active ] [ ] [ ] [ 60] activemq-console (5.4.0.fuse-00-00)
[ 45] [Active ] [ ] [ ] [ 60] activemq-ra (5.4.0.fuse-00-00)
[ 46] [Active ] [ ] [ ] [ 60] activemq-pool (5.4.0.fuse-00-00)
[ 47] [Active ] [Created ] [ ] [ 60] activemq-karaf (5.4.0.fuse-00-00)
[ 52] [Resolved ] [ ] [ ] [ 60] activemq-blueprint (5.4.0.fuse-00-00)
[ 53] [Active ] [Created ] [ ] [ 60] activemq-broker.xml (0.0.0)In this example, the default broker has the bundle ID, 53.
The default broker is configured to store its data in the following directory:
InstallDir/data/activemq/defaultIf you decide that you don't want to use the default broker, you can disable it by
stopping its bundle. For example, assuming that the default broker has the bundle
ID, 53, you can disable the default broker by entering the following
console command:
osgi:stop 53
The default broker will remain de-activated, even if you stop and restart the OSGi container.
Before deploying a JMS broker, the activemq feature must be installed
in the OSGi container. Normally, this feature is installed by default in Fuse ESB, so
it should already be available. If the feature was uninstalled at some point, you
can re-install it by entering the following command at the console:
karaf@root> features:install activemq
If you have an existing Fuse Message Broker XML configuration file, you can deploy the broker by copying the configuration file into the following hot deploy directory:
InstallDir/deployAfter deploying, the configured broker starts up immediately.
After deploying the BrokerName broker, Fuse ESB
automatically creates a data directory at the following location:
InstallDir/activemq-data/BrokerName
You can hot deploy a broker using either of the following types of configuration file:
You can deploy your broker using a Spring configuration file, where the root
element is {http://www.springframework.org/schema/beans}beans. The
broker element is a child of the beans element and the
broker's namespace is http://activemq.apache.org/schema/core.
For example, the following Spring configuration instantiates a broker named
simple-spring, which creates an Openwire connector on the IP port
61000:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <broker xmlns="http://activemq.apache.org/schema/core" brokerName="simple-spring"> <transportConnectors> <transportConnector name="openwire" uri="tcp://localhost:61000"/> </transportConnectors> </broker> </beans>
It is not necessary to specify schema locations in the configuration. But if you are editing the configuration file with an XML editor, you might want to add the schema locations in order to support schema validation and content completion in the editor. For the preceding example, you could specify the schema locations as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.0.xsd"
>
...You can deploy your broker using a blueprint configuration file, where the root
element is {http://www.osgi.org/xmlns/blueprint/v1.0.0}blueprint. The
broker element is a child of the blueprint element and
the broker's namespace is
http://activemq.apache.org/schema/core.
For example, the following blueprint configuration instantiates a broker named
simple-blueprint, which creates an Openwire connector on the IP
port 61001:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <broker xmlns="http://activemq.apache.org/schema/core" brokerName="simple-blueprint"> <transportConnectors> <transportConnector name="openwire" uri="tcp://localhost:61001"/> </transportConnectors> </broker> </blueprint>
It is not necessary to specify schema locations in the configuration. But if you are editing the configuration file with an XML editor, you might want to add the schema locations in order to support schema validation and content completion in the editor. For the preceding example, you could specify the schema locations as follows:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.0.xsd
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"
>
...Blueprint is a dependency injection framework, defined by the OSGi standard, which is similar to Spring in many respects. For more details about blueprint, see The Blueprint Container.
As well as installing the libraries needed for running a Fuse Message Broker broker, the
activemq feature also provides a set of console commands for
managing and monitoring broker instances.
If you do not already have a broker configuration file, the
activemq:create-broker console command provides a simple way of
creating a new broker instance with default settings. To create a new broker with
the broker name, test, enter the following console command:
karaf@root> activemq:create-broker --name test
After executing this command, you should see the broker configuration file,
test-broker.xml, in the
directory. You can
edit the settings in this file in order to change the broker configuration. As soon
as any changes are saved to the InstallDir/deploytest-broker.xml file, the OSGi
container re-reads the broker configuration.
If the activemq-broker feature is installed in the OSGi
container, you might get an IP port conflict when you create a broker in this
way, because the default broker is also listening on IP port, 61616. To resolve
this conflict, you could either edit the test-broker.xml
configuration, to change the port, or you could disable the default broker, as
described in The Default Broker.
Broker instances created using the activemq:create-broker command are
configured to store their data in the following directory:
InstallDir/data/activemq/BrokerName
You can use the activemq:destroy-broker console command to remove a
broker instance. For example, to destroy the broker named test, enter
the following console command.
karaf@root> activemq:destroy-broker --name test
Alternatively, you can simply delete the file, test-broker.xml, from
the hot deploy directory,
.InstallDir/deploy
You can use the activemq:query console command to monitor the status
of the broker (or possibly brokers) currently deployed in the container. The
following command connects to the container's JMX port and downloads
all of the broker's status:
karaf@root> activemq:query --jmxlocal
Typically, the amount of data retrieved by this command is unwieldy. It is better
to filter the data by selecting specific queues or topics. For example, to view the
status of all queues whose name starts with camel., enter the following
command:
karaf@root> activemq:query --jmxlocal -QQueue=camel.*
For more details, see the activemq:query help text, by entering
activemq:query --help.
As an alternative to querying the broker with the activemq:query
command, you can monitor the broker through any JMX monitoring tool, such as
JConsole (a standard utility provided with Sun's Java Development Kit). For an
example of how to monitor the broker using JConsole, see JMS Endpoints in a Router Application.
The following example shows how you can integrate a JMS broker into a router
application. The example generates messages using a timer; sends the messages
through the camel.timer queue in the JMS broker; and then writes the
messages to a specific directory in the file system.
In order to run the sample router application, you need to have the
camel-activemq feature installed in the OSGi container. The
camel-activemq component is needed for defining Fuse Message Broker-based JMS
endpoints in Fuse Mediation Router. This feature is not installed by
default, so you must install it using the following console command:
karaf@root> features:install camel-activemq
You also need the activemq feature, but this feature is normally
available, because Fuse ESB installs it by default.
Most of the Fuse Mediation Router components are not installed by
default. Whenever you are about to define an endpoint in a Fuse Mediation Router route,
remember to check whether the corresponding component feature is installed.
Fuse Mediation Router component features generally have the same name as the corresponding
Fuse Mediation Router component artifact ID,
camel-.ComponentName
Example 9.1 gives an example of a Fuse Mediation Router route defined using the Spring XML DSL. Messages generated by the timer endpoint are propagated through the JMS broker and then written out to the file system.
Example 9.1. Sample Route with JMS Endpoints
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/spring"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://www.springframework.org/schema/osgi-compendium
http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="timer://MyTimer?fixedRate=true&period=4000"/>
<setBody><constant>Hello World!</constant></setBody>
<to uri="activemq:camel.timer"/>
</route>
<route>
<from uri="activemq:camel.timer"/>
<to uri="file:C:/temp/sandpit/timer"/>
</route>
</camelContext>
</beans>In general, it is necessary to create a custom instance of the Fuse Mediation Router
activemq component, because you need to specify the connection details
for connecting to the broker. The preceding example uses Spring syntax to
instantiate the activemq bean which connects to the broker URL,
tcp://localhost:61616. The broker URL must correspond to one of the
transport connectors defined in the broker configuration file,
deploy/test-broker.xml.
Example 9.1 defines two routes, as follows:
The first route uses a timer endpoint to generate messages at
four-second intervals. The setBody element places a dummy
string in the body of the message (which would otherwise be
null). The messages are then sent to the
camel.timer queue on the broker (the
activemq:camel.timer endpoint).
The activemq scheme in activemq:camel.timer
is resolved by looking up activemq in the bean registry,
which resolves to the locally instantiated bean with ID,
activemq.
The second route pulls messages off the camel.timer queue and
then writes the messages to the specified directory,
C:\temp\sandpit\timer, in the file system.
To run the sample router application, perform the following steps:
Using your favorite text editor, copy and paste the router configuration
from Example 9.1 into a file called
camel-timer.xml.
Edit the file endpoint in the second route, in order to change the target directory to a suitable location on your file system:
<route>
<from uri="activemq:camel.timer"/>
<to uri="file:YourDirectoryHere!"/>
</route>Start up a local instance of the Fuse ESB runtime by entering the following at a command prompt:
servicemix
Make sure the requisite features are installed in the OSGi container. To
install the camel-activemq feature, enter the following command
at the console:
karaf@root> features:install camel-activemq
To ensure that the activemq-broker feature is
not installed, enter the following command at the
console:
karaf@root> features:uninstall activemq-broker
Use one of the following alternatives to obtain a broker instance for this demonstration:
Use the default broker—assuming you have not disabled the default broker, you can use it for this demonstration, because it is listening on the correct port, 61616.
Create a new broker instance using the console—if you prefer not to use the default broker, you can disable it (as described in The Default Broker) and then create a new JMS broker instance by entering the following command at the console:
karaf@root> activemq:create-broker --name test
After executing this command, you should see the broker
configuration file, test-broker.xml, in the
directory.InstallDir/deploy
Hot deploy the router configuration you created in step 1. Copy the
camel-timer.xml file into the
directory.InstallDir/deploy
Within a few seconds, you should start to see files appearing in the
target directory (which is C:\temp\sandpit\timer, by default).
The file component automatically generates a unique filename for each
message that it writes.
It is also possible to monitor activity in the JMS broker by connecting to the Fuse ESB runtime's JMX port. To monitor the broker using JMX, perform the following steps:
To monitor the Fuse ESB runtime, start a JConsole instance (a standard Java utility) by entering the following command:
jconsole
Initially, a JConsole: Connect to Agent
dialog prompts you to connect to a JMX port. From the
Local tab, select the
org.apache.felix.karaf.main.Bootstrap entry and
click Connect.
In the main JConsole window, click on the
MBeans tab and then drill down to
org.apache.activemq|test|Queue in the MBean
tree (assuming that test is the name of your
broker).
Under the Queue folder, you should see the
camel.timer queue. Click on the
camel.timer queue to view statistics on the
message throughput of this queue.
To shut down the router application, delete the
camel-timer.xml file from the
directory.InstallDir/deploy
The OSGi core framework defines the OSGi Service Layer, which provides a simple mechanism for bundles to interact by registering Java objects as services in the OSGi service registry. One of the strengths of the OSGi service model is that any Java object can be offered as a service: there are no particular constraints, inheritance rules, or annotations that must be applied to the service class. This chapter describes how to deploy an OSGi service using the OSGi blueprint container.
Relative to the root of the bundle JAR file, the standard location for blueprint configuration files is the following directory:
OSGI-INF/blueprint
Any files with the suffix, .xml, under this directory are
interpreted as blueprint configuration files; in other words, any files that
match the pattern, OSGI-INF/blueprint/*.xml.
In the context of a Maven project, ProjectDir, the
standard location for blueprint configuration files is the following
directory:
ProjectDir/src/main/resources/OSGI-INF/blueprintBlueprint configuration elements are associated with the following XML namespace:
http://www.osgi.org/xmlns/blueprint/v1.0.0
The root element for blueprint configuration is blueprint, so a
blueprint XML configuration file normally has the following outline form:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> ... </blueprint>
In the blueprint root element, there is no need to specify
the location of the blueprint schema using an
xsi:schemaLocation attribute, because the schema location
is already known to the blueprint framework.
There are a few aspects of blueprint configuration that are controlled by
headers in the JAR's manifest file, META-INF/MANIFEST.MF, as
follows:
If you need to place your blueprint configuration files in a non-standard
location (that is, somewhere other than OSGI-INF/blueprint/*.xml),
you can specify a comma-separated list of alternative locations in the
Bundle-Blueprint header in the manifest file—for
example:
Bundle-Blueprint: lib/account.xml, security.bp, cnf/*.xml
Dependencies on an OSGi service are mandatory by default (although this can be
changed by setting the availability attribute to
optional on a reference element or a
reference-list element). Declaring a dependency to be mandatory
means that the bundle cannot function properly without that dependency and the
dependency must be available at all times.
Normally, while a blueprint container is initializing, it passes through a
grace period, during which time it attempts to
resolve all mandatory dependencies. If the mandatory dependencies cannot be
resolved in this time (the default timeout is 5 minutes), container
initialization is aborted and the bundle is not started. The following settings
can be appended to the Bundle-SymbolicName manifest header to
configure the grace period:
blueprint.graceperiodIf true (the default), the grace period is enabled
and the blueprint container waits for mandatory dependencies to be
resolved during initialization; if false, the grace
period is skipped and the container does not check whether the
mandatory dependencies are resolved.
blueprint.timeoutSpecifies the grace period timeout in milliseconds. The default is 300000 (5 minutes).
For example, to enable a grace period of 10 seconds, you could define the
following Bundle-SymbolicName header in the manifest file:
Bundle-SymbolicName: org.fusesource.example.osgi-client; blueprint.graceperiod:=true; blueprint.timeout:= 10000
The value of the Bundle-SymbolicName header is a semi-colon
separated list, where the first item is the actual bundle symbolic name, the
second item, blueprint.graceperiod:=true, enables the grace period
and the third item, blueprint.timeout:= 10000, specifies a 10
second timeout.
Similarly to the Spring container, the blueprint container enables you to
instantiate Java classes using a bean element. You can create all
of your main application objects this way. In particular, you can use the
bean element to create a Java object that represents an OSGi
service instance.
The blueprint bean element is defined in the blueprint schema
namespace, http://www.osgi.org/xmlns/blueprint/v1.0.0. The
blueprint {http://www.osgi.org/xmlns/blueprint/v1.0.0}bean element
should not be confused with the Spring
{http://www.springframework.org/schema/beans}bean selement,
which has a similar syntax but is defined in a different namespace.
The Spring DM specification version 2.0 or later, allows you to mix both
kinds of bean element under the beans root
element, (as long as you define each bean elements using the
appropriate namespace prefix).
The blueprint bean element enables you to create objects using a
similar syntax to the conventional Spring bean element. One
significant difference, however, is that blueprint constructor arguments are
specified using the argument child element, in contrast to Spring's
constructor-arg child element. The following example shows how
to create a few different types of bean using blueprint's bean
element:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="label" class="java.lang.String">
<argument value="LABEL_VALUE"/>
</bean>
<bean id="myList" class="java.util.ArrayList">
<argument type="int" value="10"/>
</bean>
<bean id="account" class="org.fusesource.example.Account">
<property name="accountName" value="john.doe"/>
<property name="balance" value="10000"/>
</bean>
</blueprint>Where the Account class referenced by the last bean example could
be defined as follows:
// Java
package org.fusesource.example;
public class Account
{
private String accountName;
private int balance;
public Account () { }
public void setAccountName(String name) {
this.accountName = name;
}
public void setBalance(int bal) {
this.balance = bal;
}
...
}Althought the syntax of the blueprint bean element and the Spring
bean element are similar, there are a few differences, as you
can see from Table 10.1. In
this table, the XML tags (identifiers enclosed in angle brackets) refer to child
elements of bean and the plain identifiers refer to
attributes.
Table 10.1. Comparison of Spring bean with Blueprint bean
| Spring DM Attributes/Tags | Blueprint Attributes/Tags |
|---|---|
id | id |
name/<alias> | N/A |
class | class |
scope | scope=("singleton"|"prototype") |
lazy-init=("true"|"false")
| activation=("eager"|"lazy") |
depends-on | depends-on |
init-method | init-method |
destroy-method | destroy-method |
factory-method | factory-bean |
factory-bean | factory-ref |
<constructor-arg> | <argument> |
<property> | <property> |
Where the default value of the blueprint scope attribute is
singleton and the default value of the blueprint
activation attribute is eager.
For more details on defining blueprint beans, consult the following references:
Spring Dynamic Modules Reference Guide v2.0 (see the blueprint chapters).
Section 121 Blueprint Container Specification, from the OSGi Compendium Services R4.2 specification.
This section describes how to export a Java object to the OSGi service registry, thus making it accessible as a service to other bundles in the OSGi container.
To export a service to the OSGi service registry under a single interface
name, define a service element that references the relevant service
bean, using the ref attribute, and specifies the published
interface, using the interface attribute.
For example, you could export an instance of the
SavingsAccountImpl class under the
org.fusesource.example.Account interface name using the
blueprint configuration code shown in Example 10.1.
Example 10.1. Sample Service Export with a Single Interface
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/>
<service ref="savings" interface="org.fusesource.example.Account"/>
</blueprint>Where the ref attribute specifies the ID of the corresponding
bean instance and the interface attribute specifies the name of the
public Java interface under which the service is registered in the OSGi service
registry. The classes and interfaces used in this example are shown in Example 10.2
Example 10.2. Sample Account Classes and Interfaces
// Java
package org.fusesource.example
public interface Account { ... }
public interface SavingsAccount { ... }
public interface CheckingAccount { ... }
public class SavingsAccountImpl implements SavingsAccount
{
...
}
public class CheckingAccountImpl implements CheckingAccount
{
...
}To export a service to the OSGi service registry under multiple interface
names, define a service element that references the relevant
service bean, using the ref attribute, and specifies the published
interfaces, using the interfaces child element.
For example, you could export an instance of the
SavingsAccountImpl class under the list of public Java
interfaces, org.fusesource.example.Account and
org.fusesource.example.SavingsAccount, using the following
blueprint configuration code:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/>
<service ref="savings">
<interfaces>
<value>org.fusesource.example.Account</value>
<value>org.fusesource.example.SavingsAccount</value>
</interfaces>
</service>
...
</blueprint>The interface attribute and the interfaces
element cannot be used simultaneously in the same service
element. You must use either one or the other.
If you want to export a service to the OSGi service registry under
all of its implemented public Java interfaces, there is
an easy way of accomplishing this using the auto-export
attribute.
For example, to export an instance of the SavingsAccountImpl
class under all of its implemented public interfaces, use the following
blueprint configuration code:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="savings" class="org.fusesource.example.SavingsAccountImpl"/> <service ref="savings" auto-export="interfaces"/> ... </blueprint>
Where the interfaces value of the auto-export
attribute indicates that blueprint should register all of the public interfaces
implemented by SavingsAccountImpl. The auto-export
attribute can have the following valid values:
disabledDisables auto-export. This is the default.
interfacesRegisters the service under all of its implemented public Java interfaces.
class-hierarchyRegisters the service under its own type (class) and under all
super-types (super-classes), except for the Object
class.
all-classesLike the class-hierarchy option, but including all of
the implemented public Java interfaces as well.
The OSGi service registry also allows you to associate service
properties with a registered service. Clients of the service can
then use the service properties to search for or filter services. To associate
service properties with an exported service, add a
service-properties child element that contains one or more
beans:entry elements (one beans:entry element for
each service property).
For example, to associate the bank.name string property with a
savings account service, you could use the following blueprint
configuration:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:beans="http://www.springframework.org/schema/beans"
...>
...
<service ref="savings" auto-export="interfaces">
<service-properties>
<beans:entry key="bank.name" value="HighStreetBank"/>
</service-properties>
</service>
...
</blueprint>Where the bank.name string property has the value,
HighStreetBank. It is possible to define service properties of
type other than string: that is, primitive types, arrays, and collections are
also supported. For details of how to define these types, see Controlling the Set of Advertised Properties. in the
Spring Reference Guide.
Strictly speaking, the entry element ought to belong to the
blueprint namespace. The use of the beans:entry element in
Spring's implementation of blueprint is non-standard.
There are two service properties that might be set automatically when you
export a service using the service element, as follows:
osgi.service.blueprint.compname—is always set to
the id of the service's bean element, unless
the bean is inlined (that is, the bean is defined as a child element of
the service element). Inlined beans are always
anonymous.
service.ranking—is automatically set, if the
ranking attribute is non-zero.
If a bundle looks up a service in the service registry and finds more than one
matching service, you can use ranking to determine which of the services is
returned. The rule is that, whenever a lookup matches multiple services, the
service with the highest rank is returned. The service rank can be any
non-negative integer, with 0 being the default. You can specify the
service ranking by setting the ranking attribute on the
service element—for example:
<service ref="savings" interface="org.fusesource.example.Account" ranking="10"/>If you want to keep track of service registration and unregistration events,
you can define a registration listener callback bean that
receives registration and unregistration event notifications. To define a
registration listener, add a registration-listener child element to
a service element.
For example, the following blueprint configuration defines a listener bean,
listenerBean, which is referenced by a
registration-listener element, so that the listener bean
receives callbacks whenever an Account service is registered or
unregistered:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" ...>
...
<bean id="listenerBean" class="org.fusesource.example.Listener"/>
<service ref="savings" auto-export="interfaces">
<registration-listener
ref="listenerBean"
registration-method="register"
unregistration-method="unregister"/>
</service>
...
</blueprint>Where the registration-listener element's ref
attribute references the id of the listener bean, the
registration-method attribute specifies the name of the
listener method that receives the registration callback, and
unregistration-method attribute specifies the name of the
listener method that receives the unregistration callback.
The following Java code shows a sample definition of the Listener
class that receives notifications of registration and unregistration
events:
// Java
package org.fusesource.example;
public class Listener
{
public void register(Account service, java.util.Map serviceProperties) {
...
}
public void unregister(Account service, java.util.Map serviceProperties) {
...
}
}The method names, register and unregister, are
specified by the registration-method and
unregistration-method attributes respectively. The signatures
of these methods must conform to the following syntax:
First method argument—any type T that is
assignable from the service object's type. In other words, any supertype
class of the service class or any interface implemented by the service
class. This argument contains the service instance, unless the service
bean declares the scope to be prototype, in
which case this argument is null (when the scope is
prototype, no service instance is available at
registration time).
Second method argument—must be of either
java.util.Map type or java.util.Dictionary
type. This map contains the service properties associated with this
service registration.
This section describes how to obtain and use references to OSGi services that
have been exported to the OSGi service registry. Essentially, you can use either
the reference element or the reference-list element to
import an OSGi service. The key difference between these elements is
not (as you might at first be tempted to think) that
reference returns a single service reference, while
reference-list returns a list of service references. Rather,
the real difference is that the reference element is suitable for
accessing stateless services, while the
reference-list element is suitable for accessing
stateful services.
The following models for obtaining OSGi services references are supported:
A reference manager instance is created by the
blueprint reference element. This element returns a single service
reference and is the preferred approach for accessing
stateless services. Figure 10.1 shows an overview of the
model for accessing a stateless service using the reference manager.
Beans in the client blueprint container get injected with a proxy object (the provided object), which is backed by a service object (the backing service) from the OSGi service registry. This model explicitly takes advantage of the fact that stateless services are interchangeable, in the following ways:
If multiple services instances are found that match the criteria in
the reference element, the reference manager can
arbitrarily choose one of them as the backing instance (because they are
interchangeable).
If the backing service disappears, the reference manager can immediately switch to using one of the other available services of the same type. Hence, there is no guarantee, from one method invocation to the next, that the proxy remains connected to the same backing service.
The contract between the client and the backing service is thus
stateless, and the client must not
assume that it is always talking to the same service instance. If no matching
service instances are available, the proxy will wait for a certain length of
time before throwing the ServiceUnavailable exception. The length
of the timeout is configurable by setting the timeout attribute on
the reference element.
A reference list manager instance is created by the
blueprint reference-list element. This element returns a list of
service references and is the preferred approach for accessing
stateful services. Figure 10.2 shows an overview of
the model for accessing a stateful service using the reference list
manager.
Beans in the client blueprint container get injected with a
java.util.List object (the provided
object), which contains a list of proxy objects. Each proxy is
backed by a unique service instance in the OSGi service registry. Unlike the
stateless model, backing services are not considered to be
interchangeable here. In fact, the lifecycle of each proxy in the list is
tightly linked to the lifecycle of the corresponding backing service: when a
service gets registered in the OSGi registry, a corresponding proxy is
synchronously created and added to the proxy list; and when a service gets
unregistered from the OSGi registry, the corresponding proxy is synchronously
removed from the proxy list.
The contract between a proxy and its backing service is thus
stateful, and the client may assume when it invokes
methods on a particular proxy, that it is always communicating with the
same backing service. It could happen, however, that
the backing service becomes unavailable, in which case the proxy becomes stale.
Any attempt to invoke a method on a stale proxy will generate the
ServiceUnavailable exception.
The simplest way to obtain a stateles service reference
is by specifying the interface to match, using the interface
attribute on the reference element. The service is deemed to match,
if the interface attribute value is a super-type of the service or
if the attribute value is a Java interface implemented by the service (the
interface attribute can specify either a Java class or a Java
interface).
For example, to reference a stateless SavingsAccount service (see
Example 10.1), define a
reference element as follows:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<reference id="savingsRef"
interface="org.fusesource.example.SavingsAccount"/>
<bean id="client" class="org.fusesource.example.client.Client">
<property name="savingsAccount" ref="savingsRef"/>
</bean>
</blueprint>Where the reference element creates a reference manager bean with
the ID, savingsRef. To use the referenced service, inject the
savingsRef bean into one of your client classes, as
shown.
The bean property injected into the client class can be any type that is
assignable from SavingsAccount. For example, you could define the
Client class as follows:
package org.fusesource.example.client;
import org.fusesource.example.SavingsAccount;
public class Client {
SavingsAccount savingsAccount;
// Bean properties
public SavingsAccount getSavingsAccount() {
return savingsAccount;
}
public void setSavingsAccount(SavingsAccount savingsAccount) {
this.savingsAccount = savingsAccount;
}
...
}The simplest way to obtain a stateful service reference
is by specifying the interface to match, using the interface
attribute on the reference-list element. The reference list manager
then obtains a list of all the services, whose interface attribute
value is either a super-type of the service or a Java interface implemented by
the service (the interface attribute can specify either a Java
class or a Java interface).
For example, to reference a stateful SavingsAccount service (see
Example 10.1), define a
reference-list element as follows:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<reference-list id="savingsListRef"
interface="org.fusesource.example.SavingsAccount"/>
<bean id="client" class="org.fusesource.example.client.Client">
<property name="savingsAccountList" ref="savingsListRef"/>
</bean>
</blueprint>Where the reference-list element creates a reference list manager
bean with the ID, savingsListRef. To use the referenced service
list, inject the savingsListRef bean reference into one of your
client classes, as shown.
By default, the savingsAccountList bean property is a list of
service objects (for example, java.util.List<SavingsAccount>).
You could define the client class as follows:
package org.fusesource.example.client;
import org.fusesource.example.SavingsAccount;
public class Client {
java.util.List<SavingsAccount> accountList;
// Bean properties
public java.util.List<SavingsAccount> getSavingsAccountList() {
return accountList;
}
public void setSavingsAccountList(
java.util.List<SavingsAccount> accountList
) {
this.accountList = accountList;
}
...
}To match both the interface and the component name (bean ID) of a
stateless service, specify both the
interface attribute and the component-name
attribute on the reference element, as follows:
<reference id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
component-name="savings"/>To match both the interface and the component name (bean ID) of a
stateful service, specify both the
interface attribute and the component-name
attribute on the reference-list element, as follows:
<reference-list id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
component-name="savings"/>You can select services by matching service properties against a filter. The
filter is specified using the filter attribute on the
reference element or on the reference-list
element. The value of the filter attribute must be an
LDAP filter expression. For example, to define a
filter that matches when the bank.name service property equals
HighStreetBank, you could use the following LDAP filter
expression:
(bank.name=HighStreetBank)
To match two service property values, you can use &
conjunction, which combines expressions with a logical and.For
example, to require that the foo property is equal to
FooValue and the bar property is equal to
BarValue, you could use the following LDAP filter
expression:
(&(foo=FooValue)(bar=BarValue))
For the complete syntax of LDAP filter expressions, see section 3.2.7 of the OSGi Core Specification.
Filters can also be combined with the interface and
component-name settings, in which case all of the specified
conditions are required to match.
For example, to match a stateless service of
SavingsAccount type, with a bank.name service
property equal to HighStreetBank, you could define a
reference element as follows:
<reference id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
filter="(bank.name=HighStreetBank)"/>To match a stateful service of
SavingsAccount type, with a bank.name service
property equal to HighStreetBank, you could define a
reference-list element as follows:
<reference-list id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
filter="(bank.name=HighStreetBank)"/>By default, a reference to an OSGi service is assumed to be mandatory (see
Mandatory dependencies). It is
possible, however, to customize the dependency behavior of a
reference element or a reference-list element by
setting the availability attribute on the element. There are two
possible values of the availability attribute:
mandatory (the default), means that the dependency
must be resolved during a normal blueprint container
initialization; and optional, means that the dependency need
not be resolved during initialization.
The following example of a reference element shows how to declare
explicitly that the reference is a mandatory dependency:
<reference id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
availability="mandatory"/>To cope with the dynamic nature of the OSGi environment—for example, if
you have declared some of your service references to have optional
availability—it is often useful to track when a backing service gets bound
to the registry and when it gets unbound from the registry. To receive
notifications of service binding and unbinding events, you can define a
reference-listener element as the child of either the
reference element or the reference-list
element.
For example, the following blueprint configuration shows how to define a
reference listener as a child of the reference manager with the ID,
savingsRef:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<reference id="savingsRef"
interface="org.fusesource.example.SavingsAccount"
>
<reference-listener bind-method="onBind" unbind-method="onUnbind">
<bean class="org.fusesource.example.client.Listener"/>
</reference-listener>
</reference>
<bean id="client" class="org.fusesource.example.client.Client">
<property name="savingsAcc" ref="savingsRef"/>
</bean>
</blueprint>The preceding configuration registers an instance of
org.fusesource.example.client.Listener type as a callback that
listens for bind and unbind events. Events are
generated whenever the savingsRef reference manager's backing
service binds or unbinds.
The following example shows a sample implementation of the
Listener class:
package org.fusesource.example.client;
import org.osgi.framework.ServiceReference;
public class Listener {
public void onBind(ServiceReference ref) {
System.out.println("Bound service: " + ref);
}
public void onUnbind(ServiceReference ref) {
System.out.println("Unbound service: " + ref);
}
}The method names, onBind and onUnbind, are specified
by the bind-method and unbind-method attributes
respectively. Both of these callback methods take an
org.osgi.framework.ServiceReference argument.
This section explains how to generate, build, and deploy a simple OSGi service in the OSGi container. The service is a simple Hello World Java class and the OSGi configuration is defined using a blueprint configuration file.
In order to generate a project using the Maven Quickstart archetype, you must have the following prerequisites:
Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
The maven-archetype-quickstart archetype creates a generic Maven
project, which you can then customize for whatever purpose you like. To generate a
Maven project with the coordinates,
org.fusesource.example:osgi-service, enter the following
command:
mvn archetype:create -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.fusesource.example -DartifactId=osgi-service
The result of this command is a directory,
, containing
the files for the generated project.ProjectDir/osgi-service
Be careful not to choose a group ID for your artifact that clashes with the group ID of an existing product! This could lead to clashes between your project's packages and the packages from the existing product (because the group ID is typically used as the root of a project's Java package names).
You must customize the POM file in order to generate an OSGi bundle, as follows:
Follow the POM customization steps described in Modifying an Existing Maven Project.
In the configuration of the Maven bundle plug-in, modify the bundle
instructions to export the org.fusesource.example.service
package, as follows:
<project ... >
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
<Export-Package>org.fusesource.example.service</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>Create the
sub-directory. In this directory, use your favorite text editor to create the file,
ProjectDir/osgi-service/src/main/java/org/fusesource/example/serviceHelloWorldSvc.java, and add the code from Example 10.3 to it.
Example 10.3. The HelloWorldSvc Interface
// Java
package org.fusesource.example.service;
public interface HelloWorldSvc
{
public void sayHello();
}Create the
sub-directory. In this directory, use your favorite text editor to create the file,
ProjectDir/osgi-service/src/main/java/org/fusesource/example/service/implHelloWorldSvcImpl.java, and add the code from Example 10.4 to it.
Example 10.4. The HelloWorldSvcImpl Class
package org.fusesource.example.service.impl;
import org.fusesource.example.service.HelloWorldSvc;
public class HelloWorldSvcImpl implements HelloWorldSvc {
public void sayHello()
{
System.out.println( "Hello World!" );
}
}The blueprint configuration file is an XML file stored under the
OSGI-INF/blueprint directory on the class path. To add a blueprint
file to your project, first create the following sub-directories:
ProjectDir/osgi-service/src/main/resourcesProjectDir/osgi-service/src/main/resources/OSGI-INFProjectDir/osgi-service/src/main/resources/OSGI-INF/blueprint
Where the src/main/resources is the standard Maven location for all
JAR resources. Resource files under this directory will automatically be packaged in
the root scope of the generated bundle JAR.
Example 10.5 shows a sample blueprint file
that creates a HelloWorldSvc bean, using the bean element,
and then exports the bean as an OSGi service, using the service
element.
Under the
directory, use your favorite text editor to create the file,
ProjectDir/osgi-service/src/main/resources/OSGI-INF/blueprintconfig.xml, and add the XML code from Example 10.5.
Example 10.5. Blueprint File for Exporting a Service
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="hello" class="org.fusesource.example.service.impl.HelloWorldSvcImpl"/> <service ref="hello" interface="org.fusesource.example.service.HelloWorldSvc"/> </blueprint>
To install and run the osgi-service project, perform the following
steps:
Build the project—open a command prompt and
change directory to
. Use
Maven to build the demonstration by entering the following command:ProjectDir/osgi-service
mvn install
If this command runs successfully, the
directory should contain the bundle file,
ProjectDir/osgi-service/targetosgi-service-1.0-SNAPSHOT.jar.
Install and start the osgi-service bundle—at the Fuse ESB console, enter the following command:
karaf@root> osgi:install -s file:ProjectDir/osgi-service/target/osgi-service-1.0-SNAPSHOT.jarWhere ProjectDir is the directory containing
your Maven projects and the -s flag directs the container to
start the bundle right away. For example, if your project directory is
C:\Projects on a Windows machine, you would enter the
following command:
karaf@root> osgi:install -s file:C:/Projects/osgi-service/target/osgi-service-1.0-SNAPSHOT.jar
On Windows machines, be careful how you format the file
URL—for details of the syntax understood by the file
URL handler, see File URL Handler.
Check that the service has been created—to check that the bundle has started successfully, enter the following Fuse ESB console command:
karaf@root> osgi:list
Somewhere in this listing, you should see a line for the
osgi-service bundle, for example:
[ 236] [Active ] [Created ] [ ] [ 60] osgi-service (1.0.0.SNAPSHOT)
To check that the service is registered in the OSGi service registry, enter a console command like the following:
karaf@root> osgi:ls 236
Where the argument to the preceding command is the
osgi-service bundle ID. You should see some output like the
following at the console:
osgi-service (236) provides: ---------------------------- osgi.service.blueprint.compname = hello objectClass = org.fusesource.example.service.HelloWorldSvc service.id = 272 ---- osgi.blueprint.container.version = 1.0.0.SNAPSHOT osgi.blueprint.container.symbolicname = org.fusesource.example.osgi-service objectClass = org.osgi.service.blueprint.container.BlueprintContainer service.id = 273
This section explains how to generate, build, and deploy a simple OSGi client in
the OSGi container. The client finds the simple Hello World service in the OSGi
registry and invokes the sayHello() method on it.
In order to generate a project using the Maven Quickstart archetype, you must have the following prerequisites:
Maven installation—Maven is a free, open source build tool from Apache. You can download the latest version from http://maven.apache.org/download.html (minimum is 2.0.9).
Internet connection—whilst performing a build, Maven dynamically searches external repositories and downloads the required artifacts on the fly. In order for this to work, your build machine must be connected to the Internet.
The maven-archetype-quickstart archetype creates a generic Maven
project, which you can then customize for whatever purpose you like. To generate a
Maven project with the coordinates, org.fusesource.example:osgi-client,
enter the following command:
mvn archetype:create -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.fusesource.example -DartifactId=osgi-client
The result of this command is a directory,
, containing
the files for the generated project.ProjectDir/osgi-client
Be careful not to choose a group ID for your artifact that clashes with the group ID of an existing product! This could lead to clashes between your project's packages and the packages from the existing product (because the group ID is typically used as the root of a project's Java package names).
You must customize the POM file in order to generate an OSGi bundle, as follows:
Follow the POM customization steps described in Modifying an Existing Maven Project.
Because the client uses the HelloWorldSvc Java interface,
which is defined in the osgi-service bundle, it is necessary to
add a Maven dependency on the osgi-service bundle. Assuming
that the Maven coordinates of the osgi-service bundle are
org.fusesource.example:osgi-service:1.0-SNAPSHOT, you
should add the following dependency to the client's POM file:
<project ... >
...
<dependencies>
...
<dependency>
<groupId>org.fusesource.example</groupId>
<artifactId>osgi-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
...
</project>To add a blueprint file to your client project, first create the following sub-directories:
ProjectDir/osgi-client/src/main/resourcesProjectDir/osgi-client/src/main/resources/OSGI-INFProjectDir/osgi-client/src/main/resources/OSGI-INF/blueprint
Under the
directory, use your favorite text editor to create the file,
ProjectDir/osgi-client/src/main/resources/OSGI-INF/blueprintconfig.xml, and add the XML code from Example 10.6.
Example 10.6. Blueprint File for Importing a Service
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference id="helloWorld" interface="org.fusesource.example.service.HelloWorldSvc"/> <bean id="client" class="org.fusesource.example.client.Client" init-method="init"> <property name="helloWorldSvc" ref="helloWorld"/> </bean> </blueprint>
Where the reference element creates a reference manager that finds a
service of HelloWorldSvc type in the OSGi registry. The
bean element creates an instance of the Client class
and injects the service reference as the bean property, helloWorldSvc.
In addition, the init-method attribute specifies that the
Client.init() method is called during the bean initialization phase
(that is, after the service reference has been injected into
the client bean).
Under the
directory, use your favorite text editor to create the file,
ProjectDir/osgi-client/src/main/java/org/fusesource/example/clientClient.java, and add the Java code from Example 10.7.
Example 10.7. The Client Class
// Java
package org.fusesource.example.client;
import org.fusesource.example.service.HelloWorldSvc;
public class Client {
HelloWorldSvc helloWorldSvc;
// Bean properties
public HelloWorldSvc getHelloWorldSvc() {
return helloWorldSvc;
}
public void setHelloWorldSvc(HelloWorldSvc helloWorldSvc) {
this.helloWorldSvc = helloWorldSvc;
}
public void init() {
System.out.println("OSGi client started.");
if (helloWorldSvc != null) {
System.out.println("Calling sayHello()");
helloWorldSvc.sayHello(); // Invoke the OSGi service!
}
}
}The Client class defines a getter and a setter method for the
helloWorldSvc bean property, which enables it to receive the
reference to the Hello World service by injection. The init() method is
called during the bean initialization phase, after property injection, which means
that it is normally possible to invoke the Hello World service within the scope of
this method.
To install and run the osgi-client project, perform the following
steps:
Build the project—open a command prompt and
change directory to
. Use
Maven to build the demonstration by entering the following command:ProjectDir/osgi-client
mvn install
If this command runs successfully, the
directory should contain the bundle file,
ProjectDir/osgi-client/targetosgi-client-1.0-SNAPSHOT.jar.
Install and start the osgi-service bundle—at the Fuse ESB console, enter the following command:
karaf@root> osgi:install -s file:ProjectDir/osgi-client/target/osgi-client-1.0-SNAPSHOT.jarWhere ProjectDir is the directory containing
your Maven projects and the -s flag directs the container to
start the bundle right away. For example, if your project directory is
C:\Projects on a Windows machine, you would enter the
following command:
karaf@root> osgi:install -s file:C:/Projects/osgi-client/target/osgi-client-1.0-SNAPSHOT.jar
On Windows machines, be careful how you format the file
URL—for details of the syntax understood by the file
URL handler, see File URL Handler.
Client output—f the client bundle is started successfully, you should immediately see output like the following in the console:
Bundle ID: 239 OSGi client started. Calling sayHello() Hello World!
Fuse Mediation Router provides a simple way to invoke OSGi services using the Bean language. This feature is automatically available whenever a Fuse Mediation Router application is deployed into an OSGi container and requires no special configuration.
When a Fuse Mediation Router route is deployed into the OSGi container, the
CamelContext automatically sets up a registry chain for resolving
bean instances: the registry chain consists of the OSGi registry, followed by the
blueprint (or Spring) registry. Now, if you try to reference a particular bean class
or bean instance, the registry resolves the bean as follows:
Look up the bean in the OSGi registry first. If a class name is specified, try to match this with the interface or class of an OSGi service.
If no match is found in the OSGi registry, fall back on the blueprint registry (or the Spring registry, if you are using the Spring-DM container).
Consider the OSGi service defined by the following Java interface, which defines
the single method, getGreeting():
// Java
package org.fusesource.example.hello.boston;
public interface HelloBoston {
public String getGreeting();
}When defining the bundle that implements the HelloBoston OSGi
service, you could use the following blueprint configuration to export the
service:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="hello" class="org.fusesource.example.hello.boston.HelloBostonImpl"/>
<service ref="hello" interface="org.fusesource.example.hello.boston.HelloBoston"/>
</blueprint>Where it is assumed that the HelloBoston interface is implemented by
the HelloBostonImpl class (not shown).
After you have deployed the bundle containing the HelloBoston OSGi
service, you can invoke the service from a Fuse Mediation Router application using the Java DSL.
In the Java DSL, you invoke the OSGi service through the Bean language, as
follows:
from("timer:foo?period=5000")
.bean(org.fusesource.example.hello.boston.HelloBoston.class, "getGreeting")
.log("The message contains: ${body}")In the bean command, the first argument is the OSGi interface or
class, which must match the interface exported from the OSGi service bundle. The
second argument is the name of the bean method you want to invoke. For full details
of the bean command syntax, see Bean Integration in Implementing Enterprise Integration Patterns.
When you use this approach, the OSGi service is implicitly imported. It is not necessary to import the OSGi service explicitly in this case.
In the XML DSL, you can also use the Bean language to invoke the
HelloBoston OSGi service, but the syntax is slightly different. In
the XML DSL, you invoke the OSGi service through the Bean language, using the
method element, as follows:
<beans ...>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="timer:foo?period=5000"/>
<setBody>
<method ref="org.fusesource.example.hello.boston.HelloBoston"
method="getGreeting"/>
</setBody>
<log message="The message contains: ${body}"/>
</route>
</camelContext>
</beans>When you use this approach, the OSGi service is implicitly imported. It is not necessary to import the OSGi service explicitly in this case.
In Fuse ESB, Maven is the primary mechanism for locating artifacts and dependencies, both at build time and at run time. Normally, Maven requires Internet connectivity, so that dependencies can be downloaded from remote repositories on demand. But, as explained here, it is also possible to provide dependencies locally, so that the need for Internet connectivity is reduced.
Fuse ESB uses Maven as the primary mechanism for locating features, bundles, and their dependencies. By default, Maven is an inherently online tool: if Maven is unable to find a required artifact in its local repository, it automatically searches remote repositories on the Internet and downloads the required artifacts on demand.
If you start up a Fuse ESB console and enter the command:
karaf@root> features:list
You will see a complete list of the available features. The first column of the
listing indicates whether each feature is [installed] or
[uninstalled]. If you run this command immediatetly after
installing Fuse ESB, the installed features are the core Fuse ESB
features. These core features and all of their dependencies are
provided in the Fuse ESB installation under the following directory:
EsbInstallDir/systemIn other words, none of the core features need to fetched online, they are all provided in the system repository.
If you run features:list immediately after installing Fuse ESB, the
features listed as uninstalled are the optional Fuse ESB features.
In contrast to the core features, the optional features are not
provided in the system repository. If you try to install one of the optional
features, for example:
karaf@root> features:install camel-jms
The Fuse ESB container now attempts to locate and to download the
camel-jms feature and, all of its dependencies, from remote
repositories over the Internet.
If you are working in an offline environment, you need to make sure that all of the Fuse ESB features you require are available locally, from internal repositories. One way to achieve this is to create a smaller custom offline repository, which contains just the features and artifacts you need to run your application. For more details, see Generating a Custom Offline Repository.
This section explains how Maven locates artifacts at build time. Essentially, Maven implements a simple caching scheme: artifacts are downloaded from remote repositories on the Internet and then cached in the local repository. Figure 11.1 shows an overview of the procedure that Maven follows when locating artifacts at build time.
While building a project, Maven locates required artifacts (dependencies, required plugins, and so on) as follows:
The first place that Maven looks for artifacts is in the local
repository, which is the local cache where Maven stores all
of the artifacts it has downloaded or found elsewhere. The default location
of the local repository is the .m2/repository/ directory under
the user's home directory.
If an artifact is not available in the local repository, Maven has an ordered list repositories, from which it can try to download the artifact. This list of repositories can include both internal and remote repositories. Normally, any internal repositories (that is, repositories maintained in the local network) should appear at the head of the repository list, so that they are consulted first.
If the artifact is not available from local or internal repositories, the next repositories to try are the remote repositories (which are accessible, for example, through the HTTP or the HTTPS protocols).
When a Maven project is built using the mvn install command,
the project itself is installed into the local repository.
You can configure the following kinds of repository for locating Maven artifacts at build time:
Maven resolves the location of the local repository, by checking the following settings:
The location specified by the localRepository element in the
~/.m2/settings.xml file (UNIX and Linux) or
C:\Documents and
Settings\
(Windows).UserName\.m2\settings.xml
Otherwise, the location specified by the localRepository
element in the
file.M2_HOME/conf/settings.xml
Otherwise, the default location is in the user's home directory,
~/.m2/repository/ (UNIX and Linux) or C:\Documents
and Settings\
(Windows).UserName\.m2\repository
Maven enables you to specify the location of internal repositories either in your
settings.xml file (which applies to all projects) or in a
pom.xml (which applies to that project only). Typically, the
location of an internal repository is specified using either a file://
URL or a http:// URL (assuming you have set up a local Web server to
serve up the artifacts) and you should generally ensure that internal repositories
are listed before remote repositories. Otherwise, there is
nothing special about an internal repository: it is just a repository that happens
to be located in your internal network.
For an example of how to specify a repository in your settings.xml
file, see Adding the fusesource repository.
This section explains how the Fuse ESB container locates artifacts at run time. The container strikes a balance between accessing artifacts locally and downloading artifacts from remote repositories: all of the features installed by default are provided locally, in the system repository; whereas non-default features are downloaded from remote repositories the first time they are used. Figure 11.2 shows an overview of the procedure that Fuse ESB follows when locating artifacts at run time.
Whenever a feature is installed (using features:install) or a Maven
artifact is installed (using osgi:install
) at run time, the Fuse ESB container
locates the required Maven artifacts as follows:MvnURL
The first place the container looks for artifacts is the system
repository, which contains all of the artifacts (bundles, features, and so
on) provided with the standard Fuse ESB installation. The system repository is
located at
.EsbInstallDir/system
If an artifact is not available in the system repository, the container searches any other configured default repositories. In practice, this could be a custom repository.
If the artifact is not available in the system or default repositories,
the container searches the Maven local repository. The default location of
the local repository is the .m2/repository/ directory under the
user's home directory.
If the artifact is not available in the system, default, or local
repositories, the next repositories to try are the remote repositories
(which are specified in the Fuse ESB configuration file,
etc/org.ops4j.pax.url.mvn.cfg).
If an artifact is found in a remote repository, it is automatically downloaded and installed into the local repository.
You can configure the following kinds of repository for locating Maven artifacts at run time:
The default repositories are always checked first. After installing Fuse ESB, the
list of default repositories contains just a single entry, for the Fuse ESB system
repository. For example, the etc/org.ops4j.pax.url.mvn.cfg file
contains the following setting to configure the default repository list:
org.ops4j.pax.url.mvn.defaultRepositories=file:${karaf.home}/${karaf.default.repository}@snapshotsThe defaultRepositories property takes a comma-separated list,
enabling you to specify multiple default repositories. You can specify the
repository location using a URL with a file:, http:, or
https: scheme and you can optionally add the
@snapshots suffix to allow snapshot versions to be read from the
repository.
The container resolves the location of the local repository, by checking the following settings:
The location specified by the localRepository element in the
Maven settings file, whose location is specified by the
org.ops4j.pax.url.mvn.settings property in the
etc/org.ops4j.pax.url.mvn.cfg file.
Otherwise, the location specified by the localRepository
element in the ~/.m2/settings.xml file (UNIX and Linux) or
C:\Documents and
Settings\
(Windows).UserName\.m2\settings.xml
Otherwise, the location specified by the localRepository
element in the
file.M2_HOME/conf/settings.xml
Otherwise, the default location is under the user's home directory,
~/.m2/repository/ (UNIX and Linux) or C:\Documents
and Settings\
(Windows).UserName\.m2\repository
The remote repositories checked by the container are specified by the
org.ops4j.pax.url.mvn.repositories property in the
etc/org.ops4j.pax.url.mvn.cfg file. The repositories are specified
in the form of a comma-separated list—for example:
org.ops4j.pax.url.mvn.repositories= \
http://repo1.maven.org/maven2, \
http://repo.fusesource.com/maven2, \
http://repo.fusesource.com/maven2-snapshot@snapshots@noreleases, \
http://repo.fusesource.com/nexus/content/repositories/releases, \
http://repo.fusesource.com/nexus/content/repositories/snapshots@snapshots@noreleases, \
http://repository.apache.org/content/groups/snapshots-group@snapshots@noreleases, \
http://repository.ops4j.org/maven2, \
http://svn.apache.org/repos/asf/servicemix/m2-repo, \
http://repository.springsource.com/maven/bundles/release, \
http://repository.springsource.com/maven/bundles/externalA repository URL in this list can optionally include one or more of the following suffixes:
@snapshotSnapshot versions can be downloaded from this repository.
@noreleasesRelease versions cannot be downloaded from this repository.
When you move from the development phase of a project to the deployment phase, it is typically more convenient to pre-install all of the artifacts required by your application, rather than downloading them from the Internet on demand. In this case, the ideal solution is to create a custom offline repository, which contains the artifacts needed for your deployment. Creating a custom offline repository by hand, however, would be difficult, because it would need to include all of the transitive dependencies associated with your application bundles and features.
The ideal way to create a custom offline repository is to generate it, with the
help of the Apache Karaf features-maven-plugin plug-in.
The features-maven-plugin plug-in from Apache Karaf is a utility that is
used internally by the Apache Karaf developer community and the Fuse ESB development team
to create distributions of the Apache Karaf OSGi container. Some of the goals of this
plug-in are also useful for application developers, however, and this section
explains how you can use the add-features-to-repo goal to generate your
own custom offline repository.
At present, only the add-features-to-repo goal of the
features-maven-plugin plug-in is supported.
To generate and install a custom offline repository for specific Apache Karaf features, perform the following steps:
In a convenient location—for example,
—create a new
directory, ProjectDir to hold
the Maven project. Using a text editor, create the project's POM file,
ProjectDir/custom-repopom.xml, in the custom-repo directory and add the
following contents to the file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme.offline-repo</groupId>
<artifactId>custom-repo</artifactId>
<version>1.0.0</version>
<name>Generate offline features repository</name>
</project>This is the bare bones of a Maven POM, which will be added to in the following
steps. There is no need to specify a Maven package type here (it defaults to
jar), because no package will be generated for this project.
Continue editing the pom.xml and add the
features-maven-plugin as shown (where the build
element is inserted as a child of the project element):
<project ...>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>features-maven-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>add-features-to-repo</id>
<phase>generate-resources</phase>
<goals>
<goal>add-features-to-repo</goal>
</goals>
<configuration>
<descriptors>
<!-- List the URLs of required feature repositories here -->
</descriptors>
<features>
<!-- List features you want in the offline repo here -->
</features>
<repository>target/features-repo</repository>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>Subsequent steps will explain how to specify the descriptor list (of features repositories) and the features list.
In this example scenario, it is assumed that you want to make the
camel-jms feature and the camel-quartz feature
available in offline mode. List all of the features you want to download and store
in the offline repository in the features element, which is a child of
the configuration element of the
features-maven-plugin.
To make the camel-jms and camel-quartz features
available offline, add the following features element as a child of the
feature-maven-plugin's configuration element:
<features> <feature>camel-jms</feature> <feature>camel-quartz</feature> </features>
A feature repository is a location that stores feature descriptor files. Generally, because features can depend recursively on other features and because of the complexity of the dependency chains, the project normally requires access to all of the standard Fuse ESB feature repositories.
To see the full list of standard feature repositories used by your installation of
Fuse ESB, open the etc/org.apache.karaf.features.cfg configuration file
and look at the featuresRepository setting, which is a comma-separated
list of feature repositories, like the following:
... # # Comma separated list of feature repositories to register by default # featuresRepositories=mvn:org.apache.karaf/apache-karaf/2.1.3-fuse-00-00/xml/features, mvn:org.apache.servicemix.nmr/apache-servicemix-nmr/1.4.0-fuse-00-00/xml/features,mvn :org.apache.servicemix/apache-servicemix/4.3.1-fuse-00-00/xml/features,mvn:org.apache .camel.karaf/apache-camel/2.6.0-fuse-00-00/xml/features,mvn:org.apache.servicemix/ode -jbi-karaf/1.3.4/xml/features,mvn:org.apache.activemq/activemq-karaf/5.4.2-fuse-01-00 /xml/features ...
Now, add the listed feature repositories to the configuration of the
features-maven-plugin in your POM file. Open the project's
pom.xml file and add a descriptor element (as a child
of the descriptors element) for each of the standard feature
repositories. For example, given the preceding value of the
featuresRepositories list, you would define the
features-maven-plugin descriptors list in pom.xml as
follows:
<descriptors> <!-- List taken from featuresRepositories in etc/org.apache.karaf.features.cfg --> <descriptor>mvn:org.apache.karaf/apache-karaf/2.1.3-fuse-00-00/xml/features</descriptor> <descriptor>mvn:org.apache.servicemix.nmr/apache-servicemix-nmr/1.4.0-fuse-00-00/xml/features</descriptor> <descriptor>mvn:org.apache.servicemix/apache-servicemix/4.3.1-fuse-00-00/xml/features</descriptor> <descriptor>mvn:org.apache.camel.karaf/apache-camel/2.6.0-fuse-00-00/xml/features</descriptor> <descriptor>mvn:org.apache.servicemix/ode-jbi-karaf/1.3.4/xml/features</descriptor> <descriptor>mvn:org.apache.activemq/activemq-karaf/5.4.2-fuse-01-00/xml/features</descriptor> </descriptors>
Add the Fuse ESB system repository,
, to the list of
repositories in the EsbInstallDir/systempom.xml file. This is necessary for two reasons:
first of all, it saves you from downloading Maven artificats that are already
locally available from your Fuse ESB installation; and secondly, some of the artifacts
in the system repository might not be available from any of the other
repositories.
Using a text editor, open pom.xml and add the following
repositories element as a child of the project
element, customizing the file URL to point at your local system
repository:
<project ...>
...
<repositories>
<repository>
<id>esb.system.repo</id>
<name>Fuse ESB internal system repo</name>
<url>file:///E:/Programs/FUSE/apache-servicemix-4.4.1-fuse-00-08/system</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
...
</project>Generally, the project requires access to all of the standard
Fuse ESB remote repositories. To see the full list of standard remote repositories,
open the etc/org.ops4j.pax.url.mvn.cfg configuration file and look at
the org.ops4j.pax.url.mvn.repositories setting, which is a
comma-separated list of URLs like the following:
org.ops4j.pax.url.mvn.repositories= \
http://repo1.maven.org/maven2, \
http://repo.fusesource.com/maven2, \
http://repo.fusesource.com/maven2-snapshot@snapshots@noreleases, \
http://repo.fusesource.com/nexus/content/repositories/releases, \
http://repo.fusesource.com/nexus/content/repositories/snapshots@snapshots@noreleases, \
http://repository.apache.org/content/groups/snapshots-group@snapshots@noreleases, \
http://repository.ops4j.org/maven2, \
http://svn.apache.org/repos/asf/servicemix/m2-repo, \
http://repository.springsource.com/maven/bundles/release, \
http://repository.springsource.com/maven/bundles/externalEach entry in this list must be converted into a repository element,
which is then inserted as a child element of the respositories element
in the project's pom.xml file. The preceding repository URLs have
slightly different formats and must be converted as follows:
RepoURLThe value of the repository URL,
, is inserted
directly into the RepoURLurl child element of the
repository element. For example, the
http://repo1.maven.org/maven2 repository URL translates
to the following repository element:
<repository>
<!-- 'id' can be whatever you like -->
<id>repo1.maven.org</id>
<!-- 'name' can be whatever you like -->
<name>Maven central</name>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>RepoURL@snapshotsThe @snapshots suffix indicates that downloading
snapshots should be enabled for this repository. When specifying the
value of the url element, remove the
@snapshots suffix from the URL. Change the
snapshots/enabled flag to true, as shown
in the following example:
<repository> <id>IdOfRepo</id> <name>LongNameOfRepo</name> <url>RepoURL</url> <snapshots> <enabled>true</enabled> </snapshots> <releases> <enabled>true</enabled> </releases> </repository>
RepoURL@snapshots@noreleasesThe combination of the @snapshots suffix and the
@noreleases suffix indicates that downloading snapshots
should be enabled and downloading releases should be disabled for this
repository. When specifying the value of the url element,
remove both suffixes from the URL. Change the
snapshots/enabled flag to true and change
the releases/enabled flag to false, as shown
in the following example:
<repository> <id>IdOfRepo</id> <name>LongNameOfRepo</name> <url>RepoURL</url> <snapshots> <enabled>true</enabled> </snapshots> <releases> <enabled>false</enabled> </releases> </repository>
To generate the custom offline repository, open a new command prompt, change
directory to , and
enter the following Maven command:ProjectDir/custom-repo
mvn generate-resources
Assuming that the Maven build completes successfully, the custom offline repository should now be available in the following location:
ProjectDir/custom-repo/target/features-repoTo install the custom offline repository in the Fuse ESB container, edit the
etc/org.ops4j.pax.url.mvn.cfg file and append the offline
repository directory to the list of default repositories, as follows:
org.ops4j.pax.url.mvn.defaultRepositories=file:${karaf.home}/${karaf.default.repository}@snapshots,ProjectDir/custom-repo/target/features-repo@snapshotsThe @snapshots suffix can be added to the offline repository URL, if
there is a possibility that some of the artifacts in it are snapshot
versions.
Fuse ESB provides a non-standard mechanism to support asynchronous messaging, known as the Normalized Message Router (NMR), which is loosely based on the NMR defined in the JBI standard. The NMR has the advantage that it spans both the OSGi container and the JBI container. By contrast, the OSGi Event Admin Service, which also provides asynchronous communication between bundles, can only be used within the OSGi container.
Figure 12.1 shows a general overview of the NMR architecture, which spans both the OSGi container and the JBI container.
In Figure 12.1, the NMR is represented as a horizontal graphical element in order to emphasize its role linking together various application bundles. In practice, however, the NMR is deployed as a collection of bundles, just like any other application in the OSGi container.
The Fuse ESB NMR is a general-purpose message bus used for transmitting messages between bundles in the OSGi container. It is modelled on the Normalized Message Router (NMR) defined in the Java Business Integration (JBI) specification. Hence, the Fuse ESB NMR can be used to transmit XML messages, optionally augmented with properties and attachments.
Unlike the standard NMR, however, the Fuse ESB NMR is not restricted to the JBI container. You can use the NMR to transmit messages inside the OSGi container or, if the JBI container is also deployed, to transmit messages between the two containers.
A key feature of the NMR message bus is that messages are transmitted in a standard, normalized form. The JBI standard defines a normalized message, which is based on the Web Services Description Language (WSDL) message format (both WSDL 1.1 and WSDL 2.0 formats are supported). A complete normalized message has the following aspects:
Content—the main content of a normalized message must be in XML format, where the layout of a particular message is defined in a WSDL service description.
Attachments—for sending binary content, you can add attachments to the normalized message.
Properties—consist of name/value pairs.
Security subject—identifies the sender, if security features are enabled.
In the OSGi container, normalized messages have a standard layout, as follows:
Content—the main content of a normalized message, which can be in any format.
Attachments—for sending binary attachments.
Properties—consist of name/value pairs.
Security subject—identifies the sender, if security features are enabled.
When transmitting messages solely within the OSGi container, normalization of message content is not enforced. That is, the OSGi container does not impose any restrictions on the format of the message content. If messages are transmitted to an endpoint in the JBI container, however, message normalization must be observed.
Fuse ESB provides a simple Java API for accessing the NMR. You can use this API to
define endpoints that process messages received from the NMR and you can write
clients that send messages to NMR endpoints. To see how to use this API in practice,
take a look at the examples/nmr demonstration code.
To enable integration with the NMR, Fuse Mediation Router provides an NMR component, which lets
you define NMR endpoints either at the beginning (for example, as in
from("nmr:ExampleEndpoint")) or at the end (for example,
to("nmr:ExampleEndpoint")) of a route. For full details of how to
use the NMR component, see The Fuse Mediation Router NMR Component.
The NMR component is designed specifically for integrating Fuse Mediation Router with the
NMR within the OSGi container. If you deploy a Fuse Mediation Router
application in the JBI container, however, NMR integration is provided by the
JBI component. The NMR component (for use in an OSGi context) is conventionally
identified by the nmr URI scheme, whereas the JBI component (for
use in a JBI context) is conventionally identified by the jbi URI
scheme.
The NMR component is an adapter to the NMR, enabling Fuse Mediation Router applications to send messages to other bundles within the OSGi container or to components within the JBI container.
Normally, the NMR feature is pre-installed in the OSGi container. If you need to install the NMR feature, however, you can do so by entering the following console command:
karaf@root> features:install nmr
To make NMR endpoints available to your Fuse Mediation Router application, you need to create
an instance of the NMR component. Add the code shown in Example 12.1 to your bundle's Spring configuration file
(located in META-INF/spring/*.xml) in order to instantiate the NMR
component.
Example 12.1. Creating the NMR Component Bean
<?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"
xmlns:camel-osgi="http://camel.apache.org/schema/osgi"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/camel/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/osgi http://camel.apache.org/schema/osgi/camel-osgi.xsd">
<bean id="nmr" class="org.apache.servicemix.camel.nmr.ServiceMixComponent">
<property name="nmr">
<osgi:reference interface="org.apache.servicemix.nmr.api.NMR" />
</property>
</bean>
</beans>The bean element creates an instance of the NMR component with the
bean ID, nmr, where this bean ID can then be used as the scheme prefix
to create or reference NMR endpoints in your Fuse Mediation Router routes. The bean definition
references two external Java
packages—org.apache.servicemix.camel.nmr and
org.apache.servicemix.nmr.api—which must therefore be
imported by this bundle. Because the packages do not occur in Java source code, you
must add them explicitly to the list of imported packages in the bundle instructions
in the POM—see Configuring the bundle instructions for
details.
The NMR component enables you to create endpoints with the following URI format:
nmr:EndpointIdWhere EndpointId is a string that identifies the
endpoint uniquely. In particular, when used within the OSGi container, the endpoint
ID string is not restricted to have any particular format. The scheme prefix,
nmr, is actually determined by the ID of the bean that instantiates
the NMR component—for example, see Example 12.1.
If you want to use the NMR component to send messages between the OSGi container and the JBI container, you need to be aware that NMR endpoints inside the JBI container requires a special syntax for the endpoint IDs. You can address an NMR endpoint inside the JBI container using the following URI format:
nmr:JBIAddressingURIWhere JBIAddressingURI conforms to the URI format
described in ServiceMix
URIs.
An NMR consumer endpoint automatically determines the message exchange pattern (for example, In or InOut) from the incoming message and sets the message exchange pattern in the current exchange accordingly.
The camel-nmr demonstration is located in the following directory:
InstallDir/examples/camel-nmrThe demonstration defines two routes in XML, where the routes are joined together using an NMR endpoint, as follows:
The first route is defined as follows:
At the start of the route is a timer endpoint, which
generates a heartbeat event every two seconds.
At the end of the route is an NMR endpoint, which transmits the messages to the next route.
The second route is defined as follows:
At the start of the second route is an NMR endpoint, which receives the messages sent by the first route.
Next comes a callout to a transformer bean (implemented in Java), which transforms the hearbeat into a message containing the current date and time.
At the end of the route is a log endpoint, which
sends the transformed message to Jakarta commons logger.
The route is deployed into the Fuse ESB container as an OSGi bundle.
Example 12.2 shows the routes for the
camel-nmr demonstration, taken from the Spring XML configuration
file, META-INF/spring/beans.xml.
Example 12.2. Spring XML Defining a Route with an NMR Endpoint
<?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"
xmlns:camel-osgi="http://camel.apache.org/schema/osgi"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/osgi http://camel.apache.org/schema/osgi/camel-osgi.xsd">
<import resource="classpath:org/apache/servicemix/camel/nmr/camel-nmr.xml" />
<camel-osgi:camelContext xmlns="http://camel.apache.org/schema/spring">
<!-- Route periodically sent events into the NMR -->
<route>
<from uri="timer://myTimer?fixedRate=true&period=2000"/>
<to uri="nmr:ExampleRouter"/>
</route>
<!-- Route exchange from the NMR endpoint to a log endpoint -->
<route>
<from uri="nmr:ExampleRouter"/>
<bean ref="myTransform" method="transform"/>
<to uri="log:ExampleRouter"/>
</route>
</camel-osgi:camelContext>
<bean id="myTransform" class="org.apache.servicemix.examples.camel.MyTransform">
<property name="prefix" value="MyTransform"/>
</bean>
</beans>This Spring | |
At the end of the first route, messages are sent to the NMR endpoint,
| |
When you specify an NMR endpoint in the |
Not all of the packages required by the NMR component can be automatically detected by the Maven bundle plug-in. Some of the package dependencies arise from settings in the Spring configuration file (see Example 12.1), which are not automatically taken into account by the bundle plug-in. In particular, you must ensure that the following additional packages are imported by the bundle:
org.apache.servicemix.camel.nmr
org.apache.servicemix.nmr.api
For example, the following sample configuration of the Maven bundle plug-in shows
how to add an Import-Package element that contains a list of the
packages required for the NMR component:
<project ...>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Import-Package>org.apache.servicemix.camel.nmr,org.apache.servicemix.nmr.api,*</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>The Import-Package list also includes the wildcard, *,
which instructs the bundle plug-in to scan the Java source code in order to discover
further package dependencies.
When it is time to start testing your application bundles in the OSGi container, it is recommended that you perform the testing using the Pax-Exam testing framework.
Pax-Exam is an automated testing framework for running tests in an OSGi container. Consider the manual steps that would be needed to run tests in an OSGi container:
Set up the environment and initial options for the OSGi container.
Start the OSGi container.
Install the prerequisite bundles into the OSGi container (provisioning).
Install and run the test classes.
Using Pax-Exam you can automate and simplify this testing procedure. Initialization options and provisioning options are performed by a configuration method in the test class. The Pax-Exam framework takes care of starting the OSGi container and before running the test class bundle.
This section gives a brief introduction to the Pax-Exam testing framework and explains how to write a basic example using Apache Karaf.
JUnit 4 is the latest version of the JUnit Java testing suite. What distinguishes JUnit 4 from earlier versions is that JUnit 4 defines a test by applying Java annotations (in contrast to earlier versions of JUnit, which used inherited classes and naming conventions).
The simplest JUnit tests require just two steps:
Specify which methods are the test methods by annotating them with the
@org.junit.Test annotation.
At any point in the test method, define an assertion by calling
assertTrue() with a boolean argument (you also need to
include a static import of org.junit.Assert.assertTrue() in the
file). The test succeeds, if the specified assertions all evaluate to
true.
To integrate JUnit 4 with Pax-Exam, perform the following steps:
Customize JUnit to run the test in the Pax-Exam test runner
class—JUnit allows you to delegate control over a test run to a custom
runner class (by defining a runner class that inherits from
org.junit.runner.Runner). In order to integrate JUnit with
Pax-Exam, add a @RunWith annotation to the test class as
follows:
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
...
@RunWith(JUnit4TestRunner.class)
public class MyTest {
...
}Add a Pax-Exam configuration method to the test class—in order to
run a test in an OSGi framework, you need to initialize the OSGi container
properly and install any prerequisite bundles. These essential steps are
performed by returning the appropriate options from the Pax-Exam
configuration method. This method is identified by the
@Configuration annotation as follows:
import org.ops4j.pax.exam.junit.Configuration;
...
@Configuration
public static Option[] configuration() throws Exception {
...
}Use the Pax-Exam fluent API to configure the OSGi framework—there are a fairly large number of settings and options that you can return from the Pax-Exam configuration method. In order to define these options efficiently, Pax-Exam provides a fluent API, which is defined mainly by the following classes:
org.ops4j.pax.exam.CoreOptionsProvides basic options for setting up the OSGi container. For
example, this class provides options to set Java system
properties (systemProperty()), define URLs (as a
string, url(), or as an Mvn URL,
maven()), and select OSGi frameworks
(felix() or equinox()).
org.ops4j.pax.exam.OptionUtilsProvides utilities for manipulating arrays of options and composite options.
org.ops4j.pax.exam.container.def.PaxRunnerOptionsProvides options for starting the OSGi container and for
provisioning features and bundles. For example, the
scanFeatures() and scanBundle()
methods can be used to find and install features and bundles in
the OSGi container before running the test.
Theoretically, Pax-Exam provides all of the features that are needed to run an OSGi framework embedded in Apache Karaf. In practice, however, there are a lot of Java system properties and configuration options that need to be set in order to initialize Apache Karaf. It would be a nuisance, if all of these properties and options needed to be specified explicitly in the Pax-Exam configuration method.
In order to simplify running Pax-Exam in Apache Karaf, helper classes are provided, which automatically take care of initializing the OSGi framework for you. The following classes are provided:
org.apache.karaf.testing.AbstractIntegrationTestProvides some helper methods, particularly the
getOsgiService() methods which make it easy to find an
OSGi service, by specifying the Java type of the service or by
specifying service properties.
org.apache.karaf.testing.HelperProvides the Helper.getDefaultOptions() method, which
configures all of the settings needed to start up Apache Karaf in a default
configuration.
Example 13.1 shows the Maven dependencies you need in order to run the Pax-Exam testing framework. You must specify dependencies on JUnit 4, Pax-Exam, and Apache Karaf tooling.
Example 13.1. Pax-Exam and Related Maven Dependencies
<project ...>
...
<properties>
<junit-version>4.4</junit-version>
<pax-exam-version>1.2.0</pax-exam-version>
<felix.karaf.version>1.4.0-fuse-01-00</felix.karaf.version>
...
</properties>
<dependencies>
<!-- Pax-Exam dependencies -->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
<version>${pax-exam-version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit</artifactId>
<version>${pax-exam-version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-default</artifactId>
<version>${pax-exam-version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit-extender-impl</artifactId>
<version>${pax-exam-version}</version>
</dependency>
<!-- JUnit dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit-version}</version>
</dependency>
<!-- Apache Karaf integration -->
<dependency>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>org.apache.karaf.tooling.testing</artifactId>
<version>${felix.karaf.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
...
</project>This example uses custom properties to specify the versions of the various Maven artifacts.
To learn more about using the Pax-Exam testing framework, consult the following references:
JUnit 4 testing framework on the JUnit Web site.
The Pax-Exam reference guide on the Pax-Exam Web site.
Source code for the sample FeaturesText class.
Example 13.2 shows an example of how to write a test class for
Apache Karaf in the Pax-Exam testing framework. The FeaturesText class
configures the Apache Karaf environment, installs the obr and
wrapper features, and then runs a test against the two features
(where the obr and wrapper features implement particular
sets of commands in the Apache Karaf command console).
Example 13.2. FeaturesText Class
// Java /* * Licensed to the Apache Software Foundation (ASF) * ... */ package org.apache.karaf.shell.itests; import org.apache.karaf.testing.AbstractIntegrationTest; import org.apache.karaf.testing.Helper; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.junit.Configuration; import org.ops4j.pax.exam.junit.JUnit4TestRunner; import org.osgi.service.blueprint.container.BlueprintContainer; import org.osgi.service.command.CommandProcessor; import org.osgi.service.command.CommandSession; import static org.junit.Assert.assertNotNull; import static org.ops4j.pax.exam.CoreOptions.equinox; import static org.ops4j.pax.exam.CoreOptions.felix; import static org.ops4j.pax.exam.CoreOptions.maven; import static org.ops4j.pax.exam.CoreOptions.systemProperty; import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartup; import static org.ops4j.pax.exam.OptionUtils.combine; import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.scanFeatures; import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.workingDirectory; @RunWith(JUnit4TestRunner.class)public class FeaturesTest extends AbstractIntegrationTest {
@Test
public void testFeatures() throws Exception { // Make sure the command services are available assertNotNull(getOsgiService(BlueprintContainer.class, "osgi.blueprint.container.symbolicname=org.apache.karaf.shell.obr", 20000)); assertNotNull(getOsgiService(BlueprintContainer.class, "osgi.blueprint.container.symbolicname=org.apache.karaf.shell.wrapper", 20000)); // Run some commands to make sure they are installed properly CommandProcessor cp = getOsgiService(CommandProcessor.class);
CommandSession cs = cp.createSession(System.in, System.out, System.err); cs.execute("obr:listUrl"); cs.execute("wrapper:install --help"); cs.close(); } @Configuration
public static Option[] configuration() throws Exception{ return combine(
// Default karaf environment Helper.getDefaultOptions(
// this is how you set the default log level when using pax logging (logProfile) systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("DEBUG")), // add two features scanFeatures(
maven().groupId("org.apache.karaf").artifactId("apache-felix-karaf").type("xml").classifier("features").versionAsInProject(), "obr", "wrapper" ), workingDirectory("target/paxrunner/features/"),
waitForFrameworkStartup(),
// Test on both equinox and felix equinox(), felix()
); } }
The | |
In order to integrate this JUnit test properly with Apache Karaf, you are
required to derive this test class from
The | |
The | |
This line gives an example of how to use the | |
The | |
The | |
The If there are any Java system properties in the Apache Karaf environment that
you would like to customize, you can pass the properties as optional
arguments to the | |
The Pax-Exam framework supports the concept of Apache Karaf features (see
Deploying Features). You can use the
The location of the relevant features repository is
specified by passing a Pax URL as the first argument to
| |
The | |
The | |
The options, |
There are many contexts in Fuse ESB where you need to provide a URL to specify the location of a resource (for example, as the argument to a console command). In general, when specifying a URL, you can use any of the schemes supported by Fuse ESB's built-in URL handlers. This appendix describes the syntax for all of the available URL handlers.
A file URL has the syntax, file:PathName,
where PathName is the relative or absolute pathname of a
file that is available on the Classpath. The provided
PathName is parsed by Java's built-in file
URL handler. Hence, the
PathName syntax is subject to the usual conventions
of a Java pathname: in particular, on Windows, each backslash must either be escaped
by another backslash or replaced by a forward slash.
For example, consider the pathname,
C:\Projects\camel-bundle\target\foo-1.0-SNAPSHOT.jar, on Windows.
The following example shows the correct alternatives for the
file URL on Windows:
file:C:/Projects/camel-bundle/target/foo-1.0-SNAPSHOT.jar file:C:\\Projects\\camel-bundle\\target\\foo-1.0-SNAPSHOT.jar
The following example shows some incorrect alternatives for the file URL on Windows:
file:C:\Projects\camel-bundle\target\foo-1.0-SNAPSHOT.jar // WRONG! file://C:/Projects/camel-bundle/target/foo-1.0-SNAPSHOT.jar // WRONG! file://C:\\Projects\\camel-bundle\\target\\foo-1.0-SNAPSHOT.jar // WRONG!
If you use Maven to build your bundles or if you know that a particular bundle is available from a Maven repository, you can use the Mvn handler scheme to locate the bundle.
To ensure that the Mvn URL handler can find local and remote Maven artifacts, you might find it necessary to customize the Mvn URL handler configuration. For details, see Configuring the Mvn URL handler.
An Mvn URL has the following syntax:
mvn:[repositoryUrl!]groupId/artifactId[/[version][/[packaging][/[classifier]]]]
Where repositoryUrl optionally specifies the URL of a
Maven repository. The groupId,
artifactId, version,
packaging, and classifier
are the standard Maven coordinates for locating Maven artifacts (see Maven coordinates).
When specifying an Mvn URL, only the groupId and the
artifactId coordinates are required. The following
examples reference a Maven bundle with the groupId,
org.fusesource.example, and with the
artifactId, bundle-demo:
mvn:org.fusesource.example/bundle-demo mvn:org.fusesource.example/bundle-demo/1.1
When the version is omitted, as in the first example,
it defaults to LATEST, which resolves to the latest version based on
the available Maven metadata.
In order to specify a classifier value without
specifying a packaging or a
version value, it is permissible to leave gaps in the
Mvn URL. Likewise, if you want to specify a packaging
value without a version value. For example:
mvn:groupId/artifactId///classifiermvn:groupId/artifactId/version//classifiermvn:groupId/artifactId//packaging/classifiermvn:groupId/artifactId//packaging
When specifying the version value in an Mvn URL, you
can specify a version range (using standard Maven version range syntax) in place of
a simple version number. You use square brackets—[ and
]—to denote inclusive ranges and
parentheses—( and )—to denote exclusive
ranges. For example, the range, [1.0.4,2.0), matches any version,
v, that satisfies 1.0.4 <= v < 2.0. You can use
this version range in an Mvn URL as follows:
mvn:org.fusesource.example/bundle-demo/[1.0.4,2.0)
Before using Mvn URLs for the first time, you might need to customize the Mvn URL handler settings, as follows:
The Mvn URL handler resolves a reference to a local Maven repository and maintains a list of remote Maven repositories. When resolving an Mvn URL, the handler searches first the local repository and then the remote repositories in order to locate the specified Maven artifiact. If there is a problem with resolving an Mvn URL, the first thing you should do is to check the handler settings to see which local repository and remote repositories it is using to resolve URLs.
To check the Mvn URL settings, enter the following commands at the console:
karaf@root> config:edit org.ops4j.pax.url.mvn karaf@root> config:proplist
The config:edit command switches the focus of the config
utility to the properties belonging to the org.ops4j.pax.url.mvn
persistent ID. The config:proplist command outputs all of the property
settings for the current persistent ID. With the focus on
org.ops4j.pax.url.mvn, you should see a listing similar to the
following:
org.ops4j.pax.url.mvn.localRepository = file:E:/Data/.m2/repository service.pid = org.ops4j.pax.url.mvn org.ops4j.pax.url.mvn.defaultRepositories = file:E:/Programs/FUSE/apache-serv icemix-4.2.0-fuse-SNAPSHOT/system@snapshots felix.fileinstall.filename = org.ops4j.pax.url.mvn.cfg org.ops4j.pax.url.mvn.repositories = http://repo1.maven.org/maven2, http://re po.fusesource.com/maven2, http://repo.fusesource.com/maven2-snapshot@snapshots@n oreleases, http://repository.apache.org/content/groups/snapshots-group@snapshots @noreleases, http://repository.ops4j.org/maven2, http://svn.apache.org/repos/asf /servicemix/m2-repo, http://repository.springsource.com/maven/bundles/release, h ttp://repository.springsource.com/maven/bundles/external
Where the localRepository setting shows the local repository location
currently used by the handler and the repositories setting shows the
remote repository list currently used by the handler.
To customize the property settings for the Mvn URL handler, edit the following configuration file:
InstallDir/etc/org.ops4j.pax.url.mvn.cfgThe settings in this file enable you to specify explicitly the location of the local Maven repository, remove Maven repositories, Maven proxy server settings, and more. Please see the comments in the configuration file for more details about these settings.
In particular, if your local Maven repository is in a non-default location, you
might find it necessary to configure it explicitly in order to access Maven
artifacts that you build locally. In your org.ops4j.pax.url.mvn.cfg
configuration file, uncomment the org.ops4j.pax.url.mvn.localRepository
property and set it to the location of your local Maven repository. For example:
# Path to the local maven repository which is used to avoid downloading # artifacts when they already exist locally. # The value of this property will be extracted from the settings.xml file # above, or defaulted to: # System.getProperty( "user.home" ) + "/.m2/repository" # org.ops4j.pax.url.mvn.localRepository=file:E:/Data/.m2/repository
For more details about the mvn URL syntax, see the original Pax URL
Mvn
Protocol documentation.
If you need to reference a JAR file that is not already packaged as a bundle, you can use the Wrap URL handler to convert it dynamically. The implementation of the Wrap URL handler is based on Peter Krien's open source Bnd utility.
A Wrap URL has the following syntax:
wrap:locationURL[,instructionsURL][$instructions]
The locationURL can be any URL that locates a JAR
(where the referenced JAR is not formatted as a bundle). The
optional instructionsURL references a Bnd properties file
that specifies how the bundle conversion is performed. The optional
instructions is an ampersand, &,
delimited list of Bnd properties that specify how the bundle conversion is
performed.
In most cases, the default Bnd instructions are adequate for wrapping an API JAR
file. By default, Wrap adds manifest headers to the JAR's
META-INF/Manifest.mf file as shown in Table A.1.
Table A.1. Default Instructions for Wrapping a JAR
| Manifest Header | Default Value |
|---|---|
Import-Package | *;resolution:=optional |
Export-Package | All packages from the wrapped JAR. |
Bundle-SymbolicName | The name of the JAR file, where any characters not in the set
[a-zA-Z0-9_-] are replaced by underscore,
_. |
The following Wrap URL locates version 1.1 of the commons-logging JAR
in a Maven repository and converts it to an OSGi bundle using the default Bnd
properties:
wrap:mvn:commons-logging/commons-logging/1.1
The following Wrap URL uses the Bnd properties from the file,
E:\Data\Examples\commons-logging-1.1.bnd:
wrap:mvn:commons-logging/commons-logging/1.1,file:E:/Data/Examples/commons-logging-1.1.bnd
The following Wrap URL specifies the Bundle-SymbolicName property and
the Bundle-Version property explicitly:
wrap:mvn:commons-logging/commons-logging/1.1$Bundle-SymbolicName=apache-comm-log&Bundle-Version=1.1
If the preceding URL is used as a command-line argument, it might be necessary to
escape the dollar sign, \$, to prevent it from being processed by the
command line, as follows:
wrap:mvn:commons-logging/commons-logging/1.1\$Bundle-SymbolicName=apache-comm-log&Bundle-Version=1.1
For more details about the wrap URL handler, see the following
references:
The Bnd tool documentation, for more details about Bnd properties and Bnd instruction files.
The original Pax URL Wrap Protocol documentation.
If you need to deploy a WAR file in an OSGi container, you can automatically add
the requisite manifest headers to the WAR file by prefixing the WAR URL with
war:, as described here.
A War URL is specified using either of the following syntaxes:
war:warURLwarref:instructionsURL
The first syntax, using the war scheme, specifies a WAR file that is
converted into a bundle using the default instructions. The
warURL can be any URL that locates a WAR file.
The second syntax, using the warref scheme, specifies a Bnd
properties file, instructionsURL, that contains the
conversion instructions (including some instructions that are specific to this
handler). In this syntax, the location of the referenced WAR file does
not appear explicitly in the URL. The WAR file is specified
instead by the (mandatory) WAR-URL property in the properties
file.
Some of the properties in the .bnd instructions file are specific to
the War URL handler, as follows:
WAR-URL(Mandatory) Specifies the location of the War file that is to be converted into a bundle.
Web-ContextPathSpecifies the piece of the URL path that is used to access this Web application, after it has been deployed inside the Web container.
Earlier versions of PAX Web used the property,
Webapp-Context, which is now
deprecated.
By default, the War URL handler adds manifest headers to the WAR's
META-INF/Manifest.mf file as shown in Table A.2.
Table A.2. Default Instructions for Wrapping a WAR File
| Manifest Header | Default Value |
|---|---|
Import-Package | javax.*,org.xml.*,org.w3c.* |
Export-Package | No packages are exported. |
Bundle-SymbolicName | The name of the WAR file, where any characters not in the set
[a-zA-Z0-9_-\.] are replaced by period,
.. |
Web-ContextPath | No default value. But the WAR extender
will use the value of Bundle-SymbolicName by
default. |
Bundle-ClassPath |
In addition to any class path entries specified explicitly, the following entries are added automatically:
|
The following War URL locates version 1.4.7 of the wicket-examples
WAR in a Maven repository and converts it to an OSGi bundle using the default
instructions:
war:mvn:org.apache.wicket/wicket-examples/1.4.7/war
The following Wrap URL specifies the Web-ContextPath
explicitly:
war:mvn:org.apache.wicket/wicket-examples/1.4.7/war?Web-ContextPath=wicket
The following War URL converts the WAR file referenced by the WAR-URL
property in the wicket-examples-1.4.7.bnd file and then converts the
WAR into an OSGi bundle using the other instructions in the .bnd
file:
warref:file:E:/Data/Examples/wicket-examples-1.4.7.bnd
For more details about the war URL syntax, see the original Pax URL
War
Protocol documentation.
The combination of Maven and the OSGi framework provides a sophisticated framework for building and deploying enterprise applications. In order to use this framework effectively, however, it is necessary to adopt certain conventions and best practices. The practices described in this appendix are intended to optimize the manageability and scalability of your OSGi applications.
The following best practices are recommended for OSGi related tools and utilities:
Even for a moderately sized bundle project, it is usually impractical to create and maintain a bundle Manifest by hand. The Maven bundle plug-in is the most effective tool for automating the generation of bundle Manifests in a Maven project. See Building OSGi Bundles.
Avoid using the OSGi Java API directly. Prefer a higher level technology, for example: Blueprint (from the OSGi Compendium Specification), Spring-DM, Declarative Services (DS), iPojo, and so on.
The Blueprint container is now the preferred framework for instantiating, registering, and referencing OSGi services, because this container has now been adopted as an OSGi standard. This ensures greater portability for your OSGi service definitions in the future.
Spring Dynamic Modules (Spring-DM) provided much of the original impetus for the definition of the Blueprint standard, but should now be regarded as obsolescent. Using the Blueprint container does not prevent you from using the Spring framework: the latest version of Spring is compatible with Blueprint.
When an application is composed of a large number of bundles, it becomes essential to group bundles together in order to deploy them efficiently. Apache Karaf features is a mechanism that is designed just for this purpose. It is easy to use and supported by a variety of different tools. See Deploying Features for details.
The OSGi Configuration Admin service is the preferred mechanism for providing configuration properties to your application. This configuration mechanism enjoys better tooling support than other approaches. For example, in Fuse ESB the OSGi Configuration Admin service is supported in the following ways:
Properties integrated with Spring XML files.
Properties automatically read from configuration files,
etc/persistendId.cfg
Properties can be set in feature repositories.
In order for testing to be really effective, you should run at least some of your tests in an OSGi container. This requires you to start an OSGi container, configure its environment, install prerequisite bundles, and install the actual test. Performing these steps manually for every test would make testing prohibitively difficult and time-consuming. Pax-Exam solves this problem by providing a testing framework that is capable of automatically initializing an OSGi container before running tests in the container.
See Pax-Exam Testing Framework for more details.
The following best practices are recommended when building OSGi bundles using Maven:
Use your application's package prefix as the bundle symbolic name. For example, if
all of your Java source code is located in sub-packages of
org.fusesource.fooProject, use
org.fusesource.fooProject as the bundle symbolic name.
It makes sense to identify a Maven artifact with an OSGi bundle. To show this relationship as clearly as possible, you should use base the artifact ID on the bundle symbolic name. Two conventions are commonly used:
The artifact ID is identical to the bundle symbolic name—this enables you to define the bundle symbolic name in terms of the artifact ID, using the following Maven bundle instruction:
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>The bundle symbolic name is composed of the group ID and the artifact ID, joined by a dot—this enables you to define the bundle symbolic name in terms of the group ID and the artifact ID, using the following Maven bundle instruction:
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>Properties of the form project.* can be used to reference the
value of any element in the current POM. To construct the
name of a POM property, take the XPath name of any POM element (for example,
project/artifactId) and replace occurrences of /
with the . character. Hence, ${project.artifactId}
references the artifactId element from the current POM.
One of the key advantages of the OSGi framework is its ability to manage bundle versions and the possibility of deploying multiple versions of a bundle in the same container. In order to take advantage of this capability, however, it is essential that you associate a version with any packages that you export.
For example, you can configure the maven-bundle-plugin plug-in to
export packages with the current artifact version (given by the
project.version property) as follows:
<Export-Package>
${project.artifactId}*;version=${project.version}
</Export-Package>Notice how this example exploits the convention that packages use the artifact ID,
project.artifactId, as their package prefix. The combination of
package prefix and wildcard, ${project.artifactId}*, enables you to
reference all of the source code in your bundle.
If you define any private packages in your bundle (packages that you do not want
to export), it is recommended that you identify these packages using a strict naming
convention. For example, if your bundle includes implementation classes that you do
not want to export, you should place these classes in packages prefixed by
${project.artifactId}.impl or
${project.artifactId}.internal.
If you do not specify any Export-Package instruction, the default
behavior of the Maven bundle plug-in is to exclude any packages that contain a
path segment equal to impl or internal.
To ensure that the private packages are not exported, you can
add an entry of the form ! to
the Maven bundle plug-in's export instructions. The effect of this entry is to
exclude any matching packages. For example, to exclude any packages prefixed by
PackagePattern${project.artifactId}.impl, you could add the following instruction
to the Maven bundle plug-in configuration:
<Export-Package>
!${project.artifactId}.impl.*,
${project.artifactId}*;version=${project.version}
</Export-Package>The order of entries in the Export-Package element is
significant. The first match in the list determines whether a package is
included or excluded. Hence, in order for exclusions to be effective, they
should appear at the start of the list.
In order to benefit from OSGi version management capabilities, it is important to restrict the range of acceptable versions for imported packages. You can use either of the following approaches:
Manual version ranges—you can manually specify
the version range for an imported package using the version
qualifier, as shown in the following example:
<Import-Package> org.springframework.*;version="[2.5,4)", org.apache.commons.logging.*;version="[1.1,2)", * </Import-Package>
Version ranges are specified using the standard OSGi version range syntax,
where square brackets—that is, [ and
]—denote inclusive ranges and parentheses—that is,
( and )—denote exclusive ranges. Hence
the range, [2.5,4), means that the version, v, is
restricted to the range, 2.5 <= v < 4. Note the special
case of a range written as a simple number—for example,
version="2.5", which is equivalent to the range,
[2.5,.infinity)
Automatic version ranges—if packages are imported from a Maven dependency and if the dependency is packaged as an OSGi bundle, the Maven bundle plug-in automatically adds the version range to the import instructions.
The default behavior is as follows. If your POM depends on a bundle that is identified as version 1.2.4.8, the generated manifest will import version 1.2 of the bundle's exported packages (that is, the imported version number is truncated to the first two parts, major and minor).
It is also possible to customize how imported version ranges are generated
from the bundle dependency. When setting the version property,
you can use the ${@} macro (which returns the original export
version) and the ${version} macro (which modifies a version
number) to generate a version range. For example, consider the following
version settings:
*;version="${@}"If a particular package has export version
1.2.4.8, the generated import version resolves
to 1.2.4.8.
*;version="${version;==;${@}}"If a particular package has export version
1.2.4.8, the generated import version resolves
to 1.2.
*;version="[${version;==;${@}},${version;=+;${@}})"If a particular package has export version
1.2.4.8, the generated import version range
resolves to [1.2,1.3).
*;version="[${version;==;${@}},${version;+;${@}})"If a particular package has export version
1.2.4.8, the generated import version range
resolves to [1.2,2).
The middle part of the version macro—for example, == or
=+—formats the returned version number. The equals
sign, =, returns the corresponding version part unchanged; the
plus sign, +, returns the corresponding version part plus one;
and the minus sign, -, returns the corresponding version part
minus one. For more details, consult the Bnd documentation for the version macro and
the -versionpolicy option.
In practice, you are likely to find that the majority of imported packages can be automatically versioned by Maven. It is, typically, only occasionally necessary to specify a version manually.
Normally, it is not good practice to import the packages that you export (though there are exceptions to this rule). Here are some guidelines to follow:
If the bundle is a pure library (providing interfaces and classes, but not instantiating any classes or OSGi services), do not import the packages that you export.
If the bundle is a pure API (providing interfaces and abstract classes, but no implementation classes), do not import the packages that you export.
If the bundle is a pure implementation (implementing and registering an OSGi service, but not providing any API), you do not need to export any packages at all.
The registered OSGi service must be accessible through an API interface or class, but it is presumed that this API is provided in a separate API bundle. The implementation bundle therefore needs to import the corresponding API packages.
A special case arises, if an implementation and its corresponding API are combined into the same bundle. In this case, the API packages must be listed amongst the export packages and amongst the import packages. This configuration is interpreted in a special way by the OSGi framework: it actually means that the API packages will either be exported or imported at run time (but not both).
The reason for this special configuration is that, in a complex OSGi application, it is possible that an API package might be provided by more than one bundle. But you do not want multiple copies of an API to be exported into OSGi, because that can lead to technical problems like class cast exceptions. When a package is listed both in the exports and in the imports, the OSGi resolver proceeds as follows:
First of all, the resolver checks whether the package has already been exported from another bundle. If so, the resolver imports the package, but does not export it.
Otherwise, the resolver uses the local API package and exports this package, but it does not import the package.
Assuming you want to avoid importing the packages that you export, there are two alternative approaches you can take, as follows:
(Recommended) The most effective way of suppressing
the import of exported packages is to append the
-noimport:=true setting to package patterns in the
Export-Package instruction. For example:
<Export-Package>
${project.artifactId}*;version=${project.version};-noimport:=true
</Export-Package>The marked packages are now not imported,
irrespective of what is contained in the Import-Package
instruction.
An alternative way of avoiding the import is to add one or more package
exclusions to the Maven bundle plug-in's Import-Package element
(this was the only possibility in earlier versions of the Maven bundle
plug-in). For example, the following Import-Package element
instructs the Maven bundle plug-in to exclude all packages prefixed by the
artifact ID, ${project.artifactId}:
<Import-Package>
!${project.artifactId}*,
org.springframework.*;version="[2.5,4)",
org.apache.commons.logging.*;version="[1.1,2)",
*
</Import-Package>When an imported package is specified with optional resolution, this allows the bundle to be resolved without resolving the optional package. This affects the resolution order of the bundles which, in turn, can affect the runtime behavior. You should therefore be careful with optional imports, in case they have some unintended side effects.
A package is optional when it appears in the Import-Package manifest
header with the resolution:="optional" setting appended to it. For
example, the following example shows an Import-Package instruction for
the Maven bundle plug-in that specifies an optional import:
<Import-Package> org.springframework.*;version="[2.5,4)", org.apache.commons.logging.*;version="[1.1,2)";resolution:="optional", * </Import-Package>
Avoid using the Require-Bundle header, if possible. The trouble with
using the Require-Bundle header is that it forces
the OSGi resolver to use packages from the specified bundle. Importing at the
granularity of packages, on the other hand, allows the resolver to be more flexible,
because there are fewer constraints: if a package is already available and resolved
from another bundle, the resolver could use that package instead.
Example B.1 shows a sample POM that illustrates the best practices for building an OSGi bundle using Maven.
Example B.1. Sample POM File Illustrating Best Practices
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.fusesource</groupId>
<artifactId>org.fusesource.fooProject</artifactId>
<packaging>bundle</packaging>
<version>1.0-SNAPSHOT</version>
<name>A fooProject OSGi Bundle</name>
<url>http://www.myorganization.org</url>
<dependencies>...</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>
!${project.artifactId}.impl.*,
${project.artifactId}*;version=${project.version};-noimport:=true
</Export-Package>
<Import-Package>
org.springframework.*;version="[2.5,4)",
org.apache.commons.logging.*;version="[1.1,2)",
*
</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>