11.4. Configuring a FAB
Overview
Typically, it is not necessary to specify any additional configuration for a FAB. But you might be interested in setting some of the optional FAB manifest headers, in order to optimize the performance of your FAB at run time. In particular, it is often a good idea to share some of the bigger dependencies, so that the FAB does not consume too much system memory in the JVM at run time.
Maven artifact pattern syntax
Many of the FAB manifest headers take a value, which is a space-separated list of patterns that match Maven artifacts. Each artifact pattern has the following syntax:
groupId[:artifactId]
If the artifact ID, artifactId, is omitted, the pattern matches all of the artifacts in the specified group, groupId. You can also use the wildcard character,
*
, to match an arbitrary sequence of characters in either the group ID or the artifact ID.
For example, to match specific artifacts, you could specify a list like the following:
org.apache.camel:camel-core org.apache.cxf:cxf-core
To match all of the Apache Camel, Apache ActiveMQ, and Apache CXF artifacts, you could specify a list like the following:
org.apache.camel org.apache.activemq org.apache.cxf
To match all Apache artifacts and all Spring framework artifacts, you could specify a list like the following:
org.apache.* org.springframework.*
Supported manifest headers
To configure a FAB, you can optionally specify any of the following FAB manifest headers:
FAB-Version-Range-Digits
FAB-Provided-Dependency
FAB-Include-Optional-Dependency
FAB-Dependency-Require-Bundle
FAB-Exclude-Dependency
Import-Package
FAB-Skip-Matching-Feature-Detection
FAB-Require-Feature-URL
,FAB-Require-Feature
FAB-Install-Provided-Bundle-Dependencies
Specifying version ranges
Maven dependencies typically specify the exact version of each required artifact. At deployment time, however, a little bit of flexibility is usually required. For example, you would normally prefer the FAB to be capable of accepting a patch update to one of its dependencies. For this reason, when the FAB is converted to a bundle at deploy time, each dependency version is normally converted into a range.
For example, given the following Maven dependency:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>2.8.1-fuse-00-02</version> <scope>provided</scope> </dependency>
When the FAB is converted to a bundle, the preceding exact version is converted to a range in the generated OSGi
Import-Package
header, as follows:
Import-Package: org.apache.camel;version="[2.8.1-fuse-00-02,2.9)"
This reflects the default range policy, where later patch versions (
2.8.2
, 2.8.3
, 2.8.4
, and so on) are accepted, but minor and major upgrades are rejected.
It is possible to modify the range policy—either to be more strict or to be more lax—by setting the
FAB-Version-Range-Digits:
manifest header to one of the following policy values:
Value | Sample | Description |
---|---|---|
0 | [2.5.6.qual,2.5.6.qual] | Exact only. |
1 | [2.5.6.qual,2.5.7) | Allow arbitrary qualifers (order not guaranteed). |
2 | [2.5.6.qual,2.6) | Allow patch releases (default). |
3 | [2.5.6.qual,3) | Allow patch and minor releases. |
4 | [2.5.6.qual,) | Any version from 2.5.6 onwards. |
Sharing dependencies
This manifest header gives you an alternative way to configure class sharing. Instead of adding
<scope>provided</scope>
to dependencies in the pom.xml
file, you can list the shared artifacts in this manifest header. The shared artifacts are specified as a space-separated list of artifact patterns, for example:
FAB-Provided-Dependency: groupId1:artifactId1 groupId2:artifactId2 ...
You can also use a wildcard,
*
, for the groupId or the artifactId. For example, to share all Apache Camel and Spring dependencies (transitively) in your FAB, add the following header to your manifest:
FAB-Provided-Dependency: org.apache.camel:* org.springframework:*
If you do not explicitly configure the
FAB-Provided-Dependency
manifest header, the FAB runtime implicitly adds the following default header:
FAB-Provided-Dependency: org.apache.camel:* org.apache.cxf:* org.apache.activemq:*
Important
If you specify the
FAB-Provided-Dependency
manifest header explicitly, you override the default header value, which would make the org.apache.camel
, org.apache.cxf
, and org.apache.activemq
dependencies unshared again. Since you normally want these dependencies to remain shared, it is good practice to include the entries, org.apache.camel:*
org.apache.cxf:*
org.apache.activemq:*
, in your custom FAB-Provided-Dependency
manifest header.
Including optional dependencies
Optional dependencies In a
pom.xml
file are marked by the presence of <optional>true</optional>
in the dependency.
By default, optional dependencies are excluded from a FAB. To force their inclusion, list them in the
FAB-Include-Optional-Dependency:
manifest header.
The header value is specified as a space-separated list of artifact patterns, with support for the wildcard,
*
. For example, to force the inclusion of all optional dependencies, add the following manifest header:
FAB-Include-Optional-Dependency: *:*
Specifying OSGi Require-Bundle
Specifies a list of artifacts which should not use the regular OSGi mechanism of importing packages, but instead should use the OSGi
Require-Bundle
directive.
By default, packages are imported from shared dependencies using the OSGi
Import-Package
directive for all artifacts not selected by the FAB-Dependency-Require-Bundle
header.
The header value is specified as a space-separated list of artifacts, with support for the wildcard,
*
—for example:
FAB-Dependency-Require-Bundle: groupId1:artifactId1 groupId2:artifactId2 ...
Excluding dependencies
To exclude specific dependencies, you can list them in the
FAB-Exclude-Dependency:
manifest header, as a space-separated list of artifact patterns, for example:
FAB-Exclude-Dependency: log4j logkit
Customising import packages
Specifying Java packages to import from the classpath, using the
Import-Package
manifest header, is the standard OSGi mechanism for specifying dependencies. Normally, you do not have to worry about imported packages when using FABs, because the FAB runtime automatically generates the Import-Package
manifest header with the requisite entries.
In exceptional cases, however, you might find that you need to import specific Java packages that FAB is unable to figure out by itself. For these cases, it is possible to add an
Import-Package
manifest header to your FAB package. At run time, FAB merges your customized import package list with the automatically generated list of import packages.
The
Import-Package
header is specified using the standard OSGi format. For example, to import the package, com.acme.special
, where the version is allowed to lie in the range [1.0, 1.1)
, you could add the following header:
Import-Package: com.acme.special;version="[1.0,1.1)"
Automatic feature detection
Red Hat JBoss Fuse comes with a collection of features that have been carefully crafted and manually adjusted so that they install exactly the right set of dependencies for a particular piece of functionality. For example, to install all of the required dependencies for the Apache Camel Jetty component, you can install the
camel-jetty
feature by entering the following console command:
JBossFuse:karaf@root> features:install camel-jetty
If you want to use the Jetty component in a FAB, you would add the following Maven dependency to the FAB's POM:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jetty</artifactId> <version>2.12.0.redhat-610379</version> <scope>provided</scope> </dependency>
If the
camel-jetty
feature is not already installed in the runtime, however, it is unlikely that the FAB mechanism would be able to install all of the required transitive dependencies successfully (after all, the camel-jetty
feature was created in the first place precisely because it has third-party dependencies that are not well integrated with OSGi or FAB).
This is where automatic feature detection comes in. By default, whenever FAB encounters a Maven dependency that matches a known feature, it automatically maps the dependency to the corresponding feature and installs the feature instead. For example, when the FAB runtime encounters the
org.apache.camel/camel-jetty
Maven dependency, it automatically maps the dependency to the camel-jetty
feature and installs the camel-jetty
feature.
Note
Automatic feature detection is not yet available for all standard features. At the time of writing, auto-detection is supported for Apache Camel component features,
camel-ComponentName
, and some Apache CXF features, cxf
, cxf-sts
, and cxf-wsn
.
Automatic feature detection is enabled by default. If you want to explicitly disable feature detection, you can set the
FAB-Skip-Matching-Feature-Detection
, specifying a space-separated list of artifact patterns. For example, to disable automatic feature detection for the Apache Camel components, add the following entry to the manifest:
FAB-Skip-Matching-Feature-Detection: org.apache.camel
Requiring features
You can configure a FAB explicitly to require one or more features, so that the features are automatically installed when you deploy the FAB into the container. To identify a feature, you must specify both the location of the features repository (normally provided in a Maven repository) and the name of the feature.
For example, to require the
cxf-sts
feature, add the following entries to the Manifest (noting that the Maven URL is continued on a second line, because it does not fit within the 72 byte line limit):
FAB-Require-Feature-URL: mvn:org.apache.cxf.karaf/apache-cxf/ 2.7.0.redhat-610379/xml/features FAB-Require-Feature: cxf-sts
The
FAB-Require-Feature-URL
header is specified as a space-separated list of URLs that give the locations of the features repositories.
The
FAB-Require-Feature
header is specified as a space-separated list of features, in the format, FeatureName
, or in the format, FeatureName/Version
. For example:
FAB-Require-Feature: cxf-sts/2.7.0.redhat-610379 cxf-wsn/2.7.0.redhat-610379
Respecting pre-installed features and bundles
There would not be much point in using features (which are manually adjusted to install exactly the right dependencies, for special cases that an automated tool could not copy with), if the FAB runtime simply forged ahead and installed all of the dependencies it thinks it needs, ignoring the dependencies that were already installed by the feature. For this reason, the FAB runtime adopts a respectful attitude towards previously installed bundles: by default, FAB does not try to install dependencies for a provided bundle, if that bundle is already installed.
The assumption is that a pre-installed bundle already has all of its transitive dependencies installed. For example, this is normally true, if the bundle was installed as part of a feature.
FAB respects pre-installed features and bundles by default. If you want to override this behavior, forcing the FAB runtime to scan the POM files in provided bundles and to install all of the transitive dependencies it finds, set the following Manifest header flag:
FAB-Install-Provided-Bundle-Dependencies: true
How to set JAR manifest headers
The Maven JAR plug-in supports the following alternative approaches to setting manifest headers in your Maven project:
In the POM file
You can specify manifest headers in
pom.xml
, by configuring the Maven JAR plug-in. In the JAR plug-in's configuration/archive/manifestEntries
element, specify manifest header settings using the following syntax:
<ManifestHeaderName>HeaderValue</ManifestHeaderName>
For example, to set the
FAB-Version-Range-Digits
manifiest header and the FAB-Provided-Dependency
manifest header in your pom.xml
file, configure the JAR plug-in as shown in Example 11.1, “Configuring FAB Manifest Headers in the POM”.
Example 11.1. Configuring FAB Manifest Headers in the POM
<project ...> ... <build> ... <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <index>true</index> <manifestEntries> <FAB-Version-Range-Digits>0</FAB-Version-Range-Digits> <FAB-Provided-Dependency> org.apache.camel:* org.apache.cxf:* org.apache.activemq:* </FAB-Provided-Dependency> </manifestEntries> </archive> </configuration> </plugin> ... </plugins> </build> </project>
In the META-INF/MANIFEST.MF file
Alternatively, you can create a
MANIFEST.MF
file directly and instruct the Maven JAR plug-in to include the provided manifest in the generated JAR (the JAR plug-in does not include the manifest by default). Configure the JAR plug-in to include the manifest file as follows:
<project ...> ... <build> ... <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <!-- lets use the default META-INF/MANIFEST.MF if its there --> <useDefaultManifestFile>true</useDefaultManifestFile> </configuration> </plugin> ... </plugins> </build> </project>
In your Maven project directory tree, create a
MANIFEST.MF
file at the following location:
ProjectDir/src/main/resources/META-INF/MANIFEST.MF
The following example shows a correctly formatted
META-INF/MANIFEST.MF
file:
FAB-Version-Range-Digits: 0 FAB-Provided-Dependency: org.apache.camel:* org.apache.cxf:* org.ac tivemq:*
Note the following peculiarities of the manifest file syntax:
- Each header is terminated by a newline.
- Line length is limited to 72 bytes (not characters), including the newline.
- Line continuation is indicated by putting a space character at the start of a line.
- The manifest file must end with a newline character.
Note
Given the awkward constraints on the manifest file syntax, it is recommended that you edit the manifest using a dedicated editor, such as the Eclipse Manifest Editor Plug-In.