OSGi Report Maven Plugin

OSGi Report Maven Plugin

The goal of org.ops4j.tools.maven:osgi-report-maven-plugin Maven plugin is to generate a report containing formatted manifests of all OSGi bundles built, packaged and installed in OSGI Maven project.

The feature of org.apache.felix:maven-bundle-plugin (and underlying BND library) is automatic generation of META-INF/MANIFEST.MF and, what's most important here, Import-Package and Export-Package entries/headers.

When working on complex OSGi / Maven applications, it's sometimes hard to track why manifest contains given version range or version. Or why some packages are added in the first place. It's however easy sometimes to break something by upgrading Maven dependency and introducing new transitive Maven dependency and eventually getting incompatible OSGi manifest change when it's not desired.

Initial goal of osgi-report-maven-plugin is to provide set of goals related to meta information about OSGi manifests generated by maven-bundle-plugin and/or BND.

Goals

For now, there's only one Maven goal: manifest-summary.

manifest-summary

This goal generates text file (and automatically attaches it as installable/deployable Maven artifact) with a content like this (example for pax-web-api bundle):

= org.ops4j.pax.logging:pax-logging-api:bundle:1.11.0-SNAPSHOT == General attributes Bnd-LastModified: 1548763677295 Build-Jdk: 1.8.0_202 Built-By: ggrzybek Created-By: Apache Maven Bundle Plugin Manifest-Version: 1.0 Tool: Bnd-1.50.0 == Bundle attributes Bundle-Activator: org.ops4j.pax.logging.internal.Activator Bundle-Description: The Pax Logging API Library is to allow for the Pax Logging Service to be reloaded without stopping the many dependent bundles. It also contains the OSGi Log Service API and the Knopflerfish Log API. Bundle-DocURL: http://www.ops4j.org/projects/pax/logging/pax-logging-api Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.html Bundle-ManifestVersion: 2 Bundle-Name: OPS4J Pax Logging - API Bundle-SymbolicName: org.ops4j.pax.logging.pax-logging-api Bundle-Vendor: OPS4J - Open Participation Software for Java Bundle-Version: 1.11.0.SNAPSHOT == Service attributes == Capabilities attributes == Package attributes Export-Package: org.apache.avalon.framework.logger provider = paxlogging version = 4.3 uses := org.apache.log org.apache.commons.logging provider = paxlogging version = 1.2.0 uses := org.ops4j.pax.logging org.osgi.framework org.apache.commons.logging provider = paxlogging version = 1.1.3 org.apache.commons.logging provider = paxlogging version = 1.0.4 org.apache.commons.logging.impl provider = paxlogging version = 1.2.0 uses := org.apache.commons.logging org.apache.commons.logging.impl provider = paxlogging version = 1.1.3 org.apache.commons.logging.impl provider = paxlogging version = 1.0.4 org.apache.juli.logging provider = paxlogging version = 5.5.28 uses := org.ops4j.pax.logging org.osgi.framework org.apache.juli.logging provider = paxlogging version = 6.0.18 org.apache.log4j provider = paxlogging version = 1.2.15 uses := org.apache.log4j.spi org.ops4j.pax.logging org.osgi.framework org.apache.log4j.spi provider = paxlogging version = 1.2.15 uses := org.apache.log4j org.apache.log4j.xml provider = paxlogging version = 1.2.15 uses := javax.xml.parsers org.w3c.dom org.apache.logging.log4j provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j.message org.apache.logging.log4j.simple org.apache.logging.log4j.spi org.apache.logging.log4j.status org.apache.logging.log4j.util org.apache.logging.log4j.message provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j org.apache.logging.log4j.status org.apache.logging.log4j.util org.apache.logging.log4j.simple provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j org.apache.logging.log4j.message org.apache.logging.log4j.spi org.apache.logging.log4j.util org.apache.logging.log4j.spi provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j org.apache.logging.log4j.message org.apache.logging.log4j.status org.apache.logging.log4j.util org.apache.logging.log4j.status provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j org.apache.logging.log4j.message org.apache.logging.log4j.simple org.apache.logging.log4j.spi org.apache.logging.log4j.util org.apache.logging.log4j.util provider = paxlogging version = 2.9.1 uses := org.apache.logging.log4j org.apache.logging.log4j.message org.apache.logging.log4j.spi org.apache.logging.log4j.status org.osgi.framework org.osgi.framework.wiring org.jboss.logging provider = paxlogging version = 3.3.0.Final uses := org.apache.logging.log4j org.apache.logging.log4j.message org.apache.logging.log4j.spi org.knopflerfish.service.log provider = paxlogging version = 1.1.0 uses := org.osgi.framework org.osgi.service.log org.ops4j.pax.logging provider = paxlogging version = 1.11.0.SNAPSHOT uses := org.knopflerfish.service.log org.osgi.framework org.osgi.service.log org.osgi.util.tracker org.ops4j.pax.logging.avalon provider = paxlogging version = 1.11.0.SNAPSHOT uses := org.apache.avalon.framework.logger org.ops4j.pax.logging org.osgi.framework org.ops4j.pax.logging.slf4j provider = paxlogging version = 1.11.0.SNAPSHOT uses := org.ops4j.pax.logging org.osgi.framework org.slf4j org.slf4j.helpers org.slf4j.spi org.ops4j.pax.logging.spi provider = paxlogging version = 1.11.0.SNAPSHOT org.osgi.service.log version = 1.3 uses := org.osgi.framework org.slf4j provider = paxlogging version = 1.7.25 uses := org.slf4j.event org.slf4j.helpers org.slf4j.spi org.slf4j provider = paxlogging version = 1.6.6 org.slf4j provider = paxlogging version = 1.5.11 org.slf4j provider = paxlogging version = 1.4.3 org.slf4j.event provider = paxlogging version = 1.7.25 uses := org.slf4j org.slf4j.helpers org.slf4j.spi org.slf4j.helpers provider = paxlogging version = 1.7.25 uses := org.slf4j org.slf4j.event org.slf4j.spi org.slf4j.helpers provider = paxlogging version = 1.6.6 org.slf4j.helpers provider = paxlogging version = 1.5.11 org.slf4j.helpers provider = paxlogging version = 1.4.3 org.slf4j.spi provider = paxlogging version = 1.7.25 uses := org.slf4j org.slf4j.spi provider = paxlogging version = 1.6.6 org.slf4j.spi provider = paxlogging version = 1.5.11 org.slf4j.spi provider = paxlogging version = 1.4.3 Import-Package: javax.xml.parsers org.apache.log resolution := optional org.ops4j.pax.logging version = [0.9.5,2.0.0) org.ops4j.pax.logging.avalon provider = paxlogging version = [0.9.5,2.0.0) org.osgi.framework version = [1.0.0,2.0.0) org.osgi.framework.wiring version = [1.0.0,2.0.0) org.osgi.service.event version = [1.0.0,2.0.0) resolution := optional org.osgi.service.log version = [1.3.0,2.0.0) org.osgi.util.tracker version = [1.0.0,2.0.0) org.w3c.dom

At first glance it's long and not very helpful. But the intent was to allow comparing such reports before & after some dependency change.

Personally I ended up installing some bundles in Apache Karaf and checking output of bundle:headers command.

Configuration

There are several options when invoking this plugin.

Simplest way is to call:

mvn install org.ops4j.tools.maven:osgi-report-maven-plugin:0.1.1:manifest-summary

inside any project that produces an artifact which is proper OSGi bundle. This will generate a report inside target/ directory and attach it as deployable artifact. See:

[INFO] [INFO] ---------------< org.ops4j.pax.logging:pax-logging-api >---------------- [INFO] Building OPS4J Pax Logging - API 1.11.0-SNAPSHOT [INFO] -------------------------------[ bundle ]------------------------------- ... [INFO] --- osgi-report-maven-plugin:0.1.0:manifest-summary (default-cli) @ pax-logging-api --- [INFO] Processing org.ops4j.pax.logging:pax-logging-api:bundle:1.11.0-SNAPSHOT [INFO] Attaching /data/sources/github.com/ops4j/org.ops4j.pax.logging/pax-logging-api/target/manifest-summary.txt [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.903 s [INFO] Finished at: 2019-01-29T13:07:57+01:00 [INFO] ------------------------------------------------------------------------

We can configure the plugin inside any POM:

<build> <plugins> <plugin> <groupId>org.ops4j.tools.maven</groupId> <artifactId>osgi-report-maven-plugin</artifactId> <version>0.1.0</version> <executions> <execution> <id>default-manifest-summary</id> <goals> <goal>manifest-summary</goal> </goals> <!-- the below configuration is the default, so can be omitted --> <configuration> <report>${project.build.directory}/manifest-summary.txt</report> <attach>true</attach> <separateReports>${project.build.directory}/reports</separateReports> </configuration> </execution> </executions> </plugin> </plugins> </build>

report and attach configuration parameters are optional and entire above <configuration> element can be skipped.

separateReports option is not enabled by default. If set to a directory, reports for individual bundles will be generated into corresponding files to make comparing the reports easier. For example, I used this option in pax-web 8.0.0-SNAPSHOT and got similar structure:

. └── org └── ops4j └── pax └── web ├── itest │   ├── pax-web-itest-base │   │   └── 8.0.0-SNAPSHOT │   │   └── pax-web-itest-base-8.0.0-SNAPSHOT.bundle.txt │   └── pax-web-itest-common │   └── 8.0.0-SNAPSHOT │   └── pax-web-itest-common-8.0.0-SNAPSHOT.jar.txt ├── pax-web-api │   └── 8.0.0-SNAPSHOT │   └── pax-web-api-8.0.0-SNAPSHOT.bundle.txt ├── pax-web-deployer │   └── 8.0.0-SNAPSHOT │   └── pax-web-deployer-8.0.0-SNAPSHOT.bundle.txt ...

And finally there's the best way to add separate module to multi-project POM.

This plugin can collect all bundles packaged within multi-module Maven project and generate summary report for all the bundles. There are however few things to consider:

  1. The report plugin has to be called last

  2. The report plugin has to be called once

  3. The report plugin has to be configured easily

Assuming project with a structure like:

project +- module 1 +- module 2 +- module 2a +- module 2b +- module 3

We have to add additional project with full POM like this:

<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> <parent> <!-- optional, but recommended parent of current multi-module project --> </parent> <artifactId>osgi-report</artifactId> <packaging>pom</packaging> <build> <plugins> <plugin> <groupId>org.ops4j.tools.maven</groupId> <artifactId>osgi-report-maven-plugin</artifactId> <version>0.1.0</version> <extensions>true</extensions> <executions> <execution> <id>default-manifest-summary</id> <goals> <goal>manifest-summary</goal> </goals> <!-- the below configuration is the default, so can be omitted --> <configuration> <report>${project.build.directory}/manifest-summary.txt</report> <attach>true</attach> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>

Remember to add osgi-report project as module to parent pom. The problem with ordering projects is resolved with this line:

<extensions>true</extensions>

This instructs Maven to load Plexus component definitions from the plugin and use them. There's only one component that's provided by the plugin - a Plexus component with role = org.apache.maven.AbstractMavenLifecycleParticipant. Such component implements afterProjectsRead() method and ensures that the module with invocation of manifest-summary is built last (by adding dynamic dependencies on all (but itself and parents) remaining projects within reactor).

With such configuration, the invocation of this plugin is a matter of:

mvn clean validate

Then, when running multi-module build (in debug mode - -X), we can see automatic dependency configuration to ensure that reporting module is run/built last:

... [INFO] Adding dependencies to MavenProject: org.ops4j.tools.maven.it:osgi-report-multi-report:0.1.0 @ /data/sources/github.com/ops4j/org.ops4j.tools/maven/osgi-report-maven-plugin/src/it/osgi-report-multi-simple/report/pom.xml [DEBUG] - Dependency {groupId=org.ops4j.tools.maven.it, artifactId=osgi-report-multi-p1, version=0.1.0, type=jar} [DEBUG] - Dependency {groupId=org.ops4j.tools.maven.it, artifactId=osgi-report-multi-p4, version=0.1.0, type=jar} [DEBUG] - Dependency {groupId=org.ops4j.tools.maven.it, artifactId=osgi-report-multi-pnested, version=0.1.0, type=pom} [DEBUG] - Dependency {groupId=org.ops4j.tools.maven.it, artifactId=osgi-report-multi-pnested-p1, version=0.1.0, type=jar} ...