Chapter 5. Building an OSGi Bundle
Abstract
This chapter describes how to build an OSGi bundle using Maven. For building bundles, the Maven bundle plug-in plays a key role, because it enables you to automate the generation of OSGi bundle headers (which would otherwise be a tedious task). Maven archetypes, which generate a complete sample project, can also provide a starting point for your bundle projects.
5.1. Generating a Bundle Project
5.1.1. Generating bundle projects with Maven archetypes
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 archetype generates a project for building OSGi bundles.
5.1.2. Apache Camel archetype
The Apache Camel 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:generate \ -DarchetypeGroupId=org.apache.camel.archetypes \ -DarchetypeArtifactId=camel-archetype-blueprint \ -DarchetypeVersion=${current-Camel-version} \ -DgroupId=GroupId \ -DartifactId=ArtifactId \ -Dversion=Version
5.1.3. Building the bundle
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.
5.2. Modifying an Existing Maven Project
5.2.1. Overview
If you already have a Maven project and you want to modify it so that it generates an OSGi bundle, perform the following steps:
5.2.2. Change the package type to bundle
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.
5.2.3. 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> <version>3.3.0</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-SymbolicName>${project.groupId}.${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.
5.2.4. Customize the bundle plug-in
For some specific recommendations on configuring the bundle plug-in for Apache CXF, see Section 5.3, “Packaging a Web Service in a Bundle”.
5.2.5. Customize the JDK compiler version
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.8, 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.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> ... </project>
5.3. Packaging a Web Service in a Bundle
5.3.1. Overview
This section explains how to modify an existing Maven project for a Apache CXF application, so that the project generates an OSGi bundle suitable for deployment in the Red Hat Fuse OSGi container. To convert the Maven project, you need to modify the project’s POM file and the project’s Blueprint file(s) (located in META-INF/spring
).
5.3.2. Modifying the POM file to generate a bundle
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 Section 5.1, “Generating a Bundle Project”.
5.3.3. Mandatory import packages
In order for your application to use the Apache CXF components, you need to import their packages into the application’s bundle. Because of the complex nature of the dependencies in Apache CXF, 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 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.springframework.beans.factory.config
5.3.4. Sample Maven bundle plug-in instructions
Example 5.1, “Configuration of Mandatory Import Packages” 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 5.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, 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.springframework.beans.factory.config, * </Import-Package> ... </instructions> </configuration> </plugin> </plugins> </build> ... </project>
5.3.5. Add a code generation plug-in
A Web services project typically requires code to be generated. Apache CXF provides two Maven plug-ins for the JAX-WS front-end, which enable tyou to integrate the code generation step into your build. The choice of plug-in depends on whether you develop your service using the Java-first approach or the WSDL-first approach, as follows:
-
Java-first approach—use the
cxf-java2ws-plugin
plug-in. -
WSDL-first approach—use the
cxf-codegen-plugin
plug-in.
5.3.6. OSGi configuration properties
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. Blueprint provides support for OSGi configuration, enabling you to substitute variables in a Blueprint file using values obtained from the OSGi Configuration Admin service.
For details of how to use OSGi configuration properties, see Section 5.3.7, “Configuring the Bundle Plug-In” and Section 9.6, “Add OSGi configurations to the feature”.
5.3.7. Configuring the Bundle Plug-In
Overview
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.
Configuration properties
Some of the commonly used configuration properties are:
Setting a bundle’s symbolic name
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 isorg.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 isorg.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 isorg.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 5.2, “Setting a bundle’s symbolic name”.
Example 5.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>
Setting a bundle’s name
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 5.3, “Setting a bundle’s name”.
Example 5.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>
Setting a bundle’s version
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 5.4, “Setting a bundle’s version”.
Example 5.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>
Specifying exported packages
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 default 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.
Specifying private packages
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 5.5, “Including a private package in a bundle” shows the configuration for including a private package in a bundle
Example 5.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>
Specifying imported packages
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 5.6, “Specifying the packages imported by a bundle” shows the configuration for specifying the packages imported by a bundle
Example 5.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>
More information
For more information on configuring a bundle plug-in, see:
5.3.8. OSGI configAdmin file naming convention
PID strings (symbolic-name syntax) allow hyphens in the OSGI specification. However, hyphens are interpreted by Apache Felix.fileinstall
and config:edit
shell commands to differentiate a "managed service" and "managed service factory". Therefore, it is recommended to not use hyphens elsewhere in a PID string.
The Configuration file names are related to the PID and factory PID.