Appendix A. Using the Maven OSGi Tooling
Appendix A. Using the Maven OSGi Tooling Abstract Manually creating a bundle, or a collection of bundles, for a large project can be cumbersome. The Maven bundle plug-in makes the job easier by automating the process and providing a number of shortcuts for specifying the contents of the bundle manifest. A.1. The Maven Bundle Plug-In The Red Hat Fuse OSGi tooling uses the Maven bundle plug-in from Apache Felix. The bundle plug-in is based on the bnd tool from Peter Kriens. It automates the construction of OSGi bundle manifests by introspecting the contents of the classes being packaged in the bundle. Using the knowledge of the classes contained in the bundle, the plug-in can calculate the proper values to populate the Import-Packages and the Export-Package properties in the bundle manifest. The plug-in also has default values that are used for other required properties in the bundle manifest. To use the bundle plug-in, do the following: Section A.2, "Setting up a Red Hat Fuse OSGi project" the bundle plug-in to your project's POM file. Section A.3, "Configuring the Bundle Plug-In" the plug-in to correctly populate your bundle's manifest. A.2. Setting up a Red Hat Fuse OSGi project Overview A Maven project for building an OSGi bundle can be a simple single level project. It does not require any sub-projects. However, it does require that you do the following: Add the bundle plug-in to your POM. Instruct Maven to package the results as an OSGi bundle. Note There are several Maven archetypes you can use to set up your project with the appropriate settings. Directory structure A project that constructs an OSGi bundle can be a single level project. It only requires that you have a top-level POM file and a src folder. As in all Maven projects, you place all Java source code in the src/java folder, and you place any non-Java resources in the src/resources folder. Non-Java resources include Spring configuration files, JBI endpoint configuration files, and WSDL contracts. Note Red Hat Fuse OSGi projects that use Apache CXF, Apache Camel, or another Spring configured bean also include a beans.xml file located in the src/resources/META-INF/spring folder. Adding a bundle plug-in Before you can use the bundle plug-in you must add a dependency on Apache Felix. After you add the dependency, you can add the bundle plug-in to the plug-in portion of the POM. Example A.1, "Adding an OSGi bundle plug-in to a POM" shows the POM entries required to add the bundle plug-in to your project. Example A.1. Adding an OSGi bundle plug-in to a POM The entries in Example A.1, "Adding an OSGi bundle plug-in to a POM" do the following: Adds the dependency on Apache Felix Adds the bundle plug-in to your project Configures the plug-in to use the project's artifact ID as the bundle's symbolic name Configures the plug-in to include all Java packages imported by the bundled classes; also imports the org.apache.camel.osgi package Configures the plug-in to bundle the listed class, but not to include them in the list of exported packages Note Edit the configuration to meet the requirements of your project. For more information on configuring the bundle plug-in, see Section A.3, "Configuring the Bundle Plug-In" . Activating a bundle plug-in To have Maven use the bundle plug-in, instruct it to package the results of the project as a bundle. Do this by setting the POM file's packaging element to bundle . Useful Maven archetypes There are several Maven archetypes available to generate a project that is preconfigured to use the bundle plug-in: the section called "Spring OSGi archetype" the section called "Apache CXF code-first archetype" the section called "Apache CXF wsdl-first archetype" the section called "Apache Camel archetype" Spring OSGi archetype The Spring OSGi archetype creates a generic project for building an OSGi project using Spring DM, as shown: You invoke the archetype using the following command: Apache CXF code-first archetype The Apache CXF code-first archetype creates a project for building a service from Java, as shown: You invoke the archetype using the following command: Apache CXF wsdl-first archetype The Apache CXF wsdl-first archetype creates a project for creating a service from WSDL, as shown: You invoke the archetype using the following command: Apache Camel archetype The Apache Camel archetype creates a project for building a route that is deployed into Red Hat Fuse, as shown: You invoke the archetype using the following command: A.3. 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: Bundle-SymbolicName Bundle-Name Bundle-Version Export-Package Private-Package Import-Package 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 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 A.2, "Setting a bundle's symbolic name" . Example A.2. Setting a bundle's symbolic name Setting a bundle's name By default, a bundle's name is set to USD{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 A.3, "Setting a bundle's name" . Example A.3. Setting a bundle's name Setting a bundle's version By default, a bundle's version is set to USD{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 A.4, "Setting a bundle's version" . Example A.4. Setting a bundle's version 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 . Important 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: 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. Important 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 A.5, "Including a private package in a bundle" shows the configuration for including a private package in a bundle Example A.5. Including a private package in a bundle 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. Important 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 A.6, "Specifying the packages imported by a bundle" shows the configuration for specifying the packages imported by a bundle Example A.6. Specifying the packages imported by a bundle More information For more information on configuring a bundle plug-in, see: olink:OsgiDependencies/OsgiDependencies Apache Felix documentation Peter Kriens' aQute Software Consultancy web site
<dependencies> <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.osgi.core</artifactId> <version>1.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Bundle-SymbolicName>USD{pom.artifactId}</Bundle-SymbolicName> <Import-Package>*,org.apache.camel.osgi</Import-Package> <Private-Package>org.apache.servicemix.examples.camel</Private-Package> </instructions> </configuration> </plugin> </plugins> </build> org.springframework.osgi/spring-bundle-osgi-archetype/1.1.2 mvn archetype:generate -DarchetypeGroupId=org.springframework.osgi -DarchetypeArtifactId=spring-osgi-bundle-archetype -DarchetypeVersion=1.1.2 -DgroupId= groupId -DartifactId= artifactId -Dversion= version org.apache.servicemix.tooling/servicemix-osgi-cxf-code-first-archetype/2010.02.0-fuse-02-00 mvn archetype:generate -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-osgi-cxf-code-first-archetype -DarchetypeVersion=2010.02.0-fuse-02-00 -DgroupId= groupId -DartifactId= artifactId -Dversion= version org.apache.servicemix.tooling/servicemix-osgi-cxf-wsdl-first-archetype/2010.02.0-fuse-02-00 mvn archetype:generate -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-osgi-cxf-wsdl-first-archetype -DarchetypeVersion=2010.02.0-fuse-02-00 -DgroupId= groupId -DartifactId= artifactId -Dversion= version org.apache.servicemix.tooling/servicemix-osgi-camel-archetype/2010.02.0-fuse-02-00 mvn archetype:generate -DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-osgi-camel-archetype -DarchetypeVersion=2010.02.0-fuse-02-00 -DgroupId= groupId -DartifactId= artifactId -Dversion= version <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Bundle-SymbolicName>USD{project.artifactId}</Bundle-SymbolicName> </instructions> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Bundle-Name>JoeFred</Bundle-Name> </instructions> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Bundle-Version>1.0.3.1</Bundle-Version> </instructions> </configuration> </plugin> !com.fuse.demo.private,com.fuse.demo.* <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> <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>