Skip to content

Conversation

@ppkarwasz
Copy link
Contributor

@ppkarwasz ppkarwasz commented Mar 15, 2025

Adds BND Maven Plugins to:

  • Generate an OSGi bundle descriptor (MANIFEST.MF).
  • Generate a JPMS bundle descriptor.
  • Check for binary compatibility between releases.

The binary compatibility check is implemented by adding the bnd-baseline-maven-plugin that checks if the version increment is compatible with the change type:

  • MICRO—only annotations added to the public API
  • MINOR—only binary compatible changes
  • MAJOR—binary incompatible changes

Fixes #96

Adds [BND Maven Plugins](https://github.com/bndtools/bnd/blob/master/maven-plugins/README.md) to:

- Generate an OSGi bundle descriptor (`MANIFEST.MF`).
- Generate a JPMS bundle descriptor.
- Check for binary compatibility between releases.

The binary compatibility check is implemented by:

- Annotating each package with `@Version`. See [Versioning in OSGi](https://bnd.bndtools.org/chapters/170-versioning.html#versions-in-osgi) for details.
- Adding the `bnd-baseline-maven-plugin` that checks if the version increment is compatible with the change type (`MICRO`—only annotations added, `MINOR`—only binary compatible changes, `MAJOR`—binary incompatible changes).
@ppkarwasz
Copy link
Contributor Author

The generated JPMS descriptor looks like this (the module name is com.github.packageurl.java:

piotr@migi:~/workspace/CycloneDX/packageurl$ jar -d -f target/packageurl-java-1.6.0-SNAPSHOT.jar 
[email protected] jar:file:///home/piotr/workspace/CycloneDX/packageurl/target/packageurl-java-1.6.0-SNAPSHOT.jar!/module-info.class
exports com.github.packageurl
exports com.github.packageurl.validator
requires jakarta.validation static
requires java.base
requires org.jspecify transitive

The generated OSGi manifest and import/exports are:

[MANIFEST packageurl-java-1.6.0-SNAPSHOT]
Bundle-Description                       The official Java implementation of the PackageURL specification. PackageURL (purl) is a minimal         specification for describing a package via a "mostly universal" URL.
Bundle-DocURL                            https://github.com/package-url/packageurl-java
Bundle-License                           MIT;link="https://opensource.org/licenses/MIT"
Bundle-ManifestVersion                   2                                       
Bundle-Name                              Package URL                             
Bundle-SCM                               url="https://github.com/package-url/packageurl-java.git",connection="scm:git:[email protected]:package-url/packageurl-java.git",developer-connection="scm:git:[email protected]:package-url/packageurl-java.git",tag=HEAD
Bundle-SymbolicName                      com.github.packageurl.java              
Bundle-Version                           1.6.0.SNAPSHOT                          
Export-Package                           com.github.packageurl;uses:="org.jspecify.annotations";version="1.6.0",com.github.packageurl.validator;uses:="jakarta.validation,org.jspecify.annotations";version="1.6.0"
Implementation-Title                     Package URL                             
Implementation-Version                   1.6.0-SNAPSHOT                          
Import-Package                           jakarta.validation;resolution:=optional;version="[3.1,4)",java.io,java.lang,java.lang.annotation,java.lang.invoke,java.net,java.nio.charset,java.util,java.util.function,java.util.stream,org.jspecify.annotations;version="[1.0,2)"
Manifest-Version                         1.0                                     
Require-Capability                       osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Specification-Title                      Package URL                             
Specification-Version                    1.6                                     

[IMPEXP]
Import-Package
  jakarta.validation                     {resolution:=optional, version=[3.1,4)}
  java.io                                
  java.lang                              
  java.lang.annotation                   
  java.lang.invoke                       
  java.net                               
  java.nio.charset                       
  java.util                              
  java.util.function                     
  java.util.stream                       
  org.jspecify.annotations               {version=[1.0,2)}
Export-Package
  com.github.packageurl                  {version=1.6.0}
  com.github.packageurl.validator        {version=1.6.0}

@dwalluck
Copy link
Contributor

I don't have much experience with OSGi bundles. How does the baseline plugin work?

How does the existing plugin compare to those offered by, e.g., org.apache.felix:maven-bundle-plugin?

@ppkarwasz
Copy link
Contributor Author

I don't have much experience with OSGi bundles. How does the baseline plugin work?

The bnd-baseline-maven-plugin downloads the previous version from the Maven Repository and for each package computes the type of changes that occurred in the public API. If you didn't increment enough a version number it fails and it gives you a list of changes:

  Name                                               Type       Delta      New        Old        Suggest    If Prov.
  org.apache.logging.log4j.core                      PACKAGE    MICRO      2.24.2     2.24.1     ok         -
  MICRO                PACKAGE    org.apache.logging.log4j.core
    CHANGED            CLASS      org.apache.logging.log4j.core.LoggerContext
      CHANGED          METHOD     getLogger(java.lang.String,org.apache.logging.log4j.message.MessageFactory)
        ADDED          ANNOTATED  org.jspecify.annotations.Nullable
      CHANGED          METHOD     hasLogger(java.lang.String,org.apache.logging.log4j.message.MessageFactory)
        ADDED          ANNOTATED  org.jspecify.annotations.Nullable
    MICRO              ANNOTATED  org.osgi.annotation.versioning.Version
      REMOVED          PROPERTY   value='2.24.1'
      ADDED            PROPERTY   value='2.24.2'
    REMOVED            VERSION    2.24.1
    ADDED              VERSION    2.24.2
  org.apache.logging.log4j.core.appender             PACKAGE    UNCHANGED  2.20.3     2.20.3     ok         -
  UNCHANGED            PACKAGE    org.apache.logging.log4j.core.appender
  org.apache.logging.log4j.core.filter               PACKAGE    MINOR      2.25.0     2.21.1     ok         -
  MINOR                PACKAGE    org.apache.logging.log4j.core.filter
    MINOR              CLASS      org.apache.logging.log4j.core.filter.StringMatchFilter$Builder
      ADDED            METHOD     setText(java.lang.String)
        ADDED          RETURN     org.apache.logging.log4j.core.filter.StringMatchFilter$Builder
    MICRO              ANNOTATED  org.osgi.annotation.versioning.Version
      REMOVED          PROPERTY   value='2.21.1'
      ADDED            PROPERTY   value='2.25.0'
    REMOVED            VERSION    2.21.1
    ADDED              VERSION    2.25.0
  ...

You can assign a version to each package explicitly with a @Version annotation or you can omit it and it inherits the version of the artifact. In Log4j we version each package separately and the version number registers in which minor version the package was changed the last time: e.g. the last method added to o.a.l.l.core.filter was in version 2.21.0 (the first with BND), in version 2.25.0 there will be another backward compatible change and somewhere in the middle an annotation was changed (hence version 2.21.1).

Since packageurl-java has two packages, it is probably worth to version them separately.

How does the existing plugin compare to those offered by, e.g., org.apache.felix:maven-bundle-plugin?

The Maven Bundle Plugin also uses BND under the hood, but often it uses an older version. Maybe this has changed recently.

IIRC there are some minor differences in the default configuration: the plugin from Apache Felix exports all packages by default, the one from BND only those explicitly mentioned or annotated with @Export.

Is com.github.packageurl.java the correct module name or should it be com.github.packageurl? The hyphen - is an invalid character for JPMS modules.

@dwalluck
Copy link
Contributor

com.github.packageurl

I think it should be this which matches the package, not the artifactId. Oddly, the maven groupId has the dash, but the package name does not (I think it is better without).

@dwalluck
Copy link
Contributor

You can assign a version to each package explicitly with a @Version annotation or you can omit it and it inherits the version of the

So it can automatically semantically version the next release, or not really? I am interested what has changed in 1.5.0 until now. Actually, I used a tool to compare 1.4.0 to 1.5.0, and saw that uriDecode just became public recently, so 1.5.0 caused some API versioning problems and I think before that was probably OK.

@ppkarwasz
Copy link
Contributor Author

com.github.packageurl

I think it should be this which matches the package, not the artifactId. Oddly, the maven groupId has the dash, but the package name does not (I think it is better without).

I changed the module name in 224769c

You can assign a version to each package explicitly with a @Version annotation or you can omit it and it inherits the version of the

So it can automatically semantically version the next release, or not really? I am interested what has changed in 1.5.0 until now. Actually, I used a tool to compare 1.4.0 to 1.5.0, and saw that uriDecode just became public recently, so 1.5.0 caused some API versioning problems and I think before that was probably OK.

From what I see, BND Baseline doesn't really generates any errors, when comparing with 1.5.0. He tries to compare package version, but version 1.5.0 was not an OSGi bundle.

japicmp could help with the first release.

Comment on lines +27 to 28
@Export
package com.github.packageurl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should likely publish the module name in the README.md.

@jeremylong jeremylong merged commit ab67b63 into package-url:master Mar 19, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Could use a module name

3 participants