4

My question is similar to this one but I am using the Maven bundle plugin to achieve the same end result.

I am building a bundle that contains a persistence.xml file and I have found that the maven-bundle-plugin automatically generates the following headers in the manifest:

Require-Capability:osgi.service;effective:=active;objectClass=javax.persistence.spi.PersistenceProvider;javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl,
Require-Capability: osgi.extender;osgi.extender=aries.jpa, 
Require-Capability: osgi.service;effective:=active;objectClass=javax.sql.DataSource;filter:="(osgi.jndi.service.name=jdbc/test)"

This in itself is not a problem however I am using Karaf and I want to deploy this and other bundles and Karaf features in one single feature of my own. When I do this it fails because the OSGi is unable to fulfil the capability osgi.service;effective:=active;objectClass=javax.persistence.spi.PersistenceProvider;javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl even though I specify the openjpa feature to be installed at the same time. I have discovered that I can get around this issue by changing effective:=active to resolution:=optional

To build my bundle I've tried the following Maven plugin configuration:

<plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>3.3.0</version>
    <extensions>true</extensions>
    <configuration>
        <instructions>
            <Export-Package>com.example
            </Export-Package>
            <Include-Resource>
                        META-INF/persistence.xml=${project.build.directory}/classes/META-INF/persistence.xml,
                        {maven-resources}
            </Include-Resource>
            <Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>
            <Require-Capability>
                        osgi.service;resolution:=optional;objectClass=javax.persistence.spi.PersistenceProvider;javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl,
                        osgi.extender;resolution:=optional;osgi.extender=aries.jpa,
                        osgi.service;resolution:=optional;objectClass=javax.sql.DataSource;filter:="(osgi.jndi.service.name=jdbc/test)"
            </Require-Capability>
        </instructions>
    </configuration>
</plugin>

However I get the same issue as in the linked question above i.e. duplicated requirements in the manifest.

I also see from a link on the above question that a change was made to bnd (bnd issue #1364) but this appears to only work for annotations? Is there a way to configure the Maven plugin to prevent duplicated requirements?

Update #1

My example code is available here at GitHub (karaf_features branch): https://github.com/jtkb/jpatest/tree/feature/karaf_features

It consists of 3 modules but only 2 are of interest for this issue, simple and simple-datasource

simple is the 'persistence unit' and contains the persistence.xml. It is also the bundle in which the 'awkward' (yet real requirements) <Require-Capability> headers are generated.

simple-datasource provides the datasource to the persistence unit and contains a Karaf feature to install simple, simple-datasource bundles and all the required 3rd party bundles (via Karaf features). The feature XML contains:

<feature name="simple-datasource" description="simple-datasource" version="1.0.0.SNAPSHOT">
    <feature version="4.1.1">jdbc</feature>
    <feature version="2.6.0">jpa</feature>
    <feature version="2.4.1">openjpa</feature>
    <feature version="1.0.1">pax-jdbc-mariadb</feature>
    <bundle>mvn:com.javatechnics.jpa/simple-datasource/1.0.0-SNAPSHOT</bundle>
    <bundle>mvn:com.javatechnics.jpa/simple/1.0.0-SNAPSHOT</bundle>
</feature>

So installing my feature in Karaf I get this error:

Error executing command: Unable to resolve root: missing requirement [root] osgi.identity; osgi.identity=simple-datasource; type=karaf.feature; version="[1.0.0.SNAPSHOT,1.0.0.SNAPSHOT]"; filter:="(&(osgi.identity=simple-datasource)(type=karaf.feature)(version>=1.0.0.SNAPSHOT)(version<=1.0.0.SNAPSHOT))"

[caused by: Unable to resolve simple-datasource/1.0.0.SNAPSHOT: missing requirement [simple-datasource/1.0.0.SNAPSHOT] osgi.identity; osgi.identity=com.javatechnics.jpa.simple; type=osgi.bundle; version="[1.0.0.SNAPSHOT,1.0.0.SNAPSHOT]"; resolution:=mandatory

[caused by: Unable to resolve com.javatechnics.jpa.simple/1.0.0.SNAPSHOT: missing requirement [com.javatechnics.jpa.simple/1.0.0.SNAPSHOT] osgi.service; objectClass=javax.persistence.spi.PersistenceProvider; javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl; effective:=active]]

The error to me almost feels like a circular reference issue but I cannot see how.

Inspecting the header of the simple bundle:

simple (59)
-----------
Bnd-LastModified = 1513115007378
Build-Jdk = 1.8.0_144
Built-By = kerry
Created-By = Apache Maven Bundle Plugin
Manifest-Version = 1.0
Meta-Persistence = META-INF/persistence.xml
Tool = Bnd-3.2.0.201605172007

Bundle-Blueprint = OSGI-INF/blueprint/blueprint.xml
Bundle-ManifestVersion = 2
Bundle-Name = simple
Bundle-SymbolicName = com.javatechnics.jpa.simple
Bundle-Version = 1.0.0.SNAPSHOT

Export-Service = 
com.javatechnics.jpa.dao.BookServiceDao;ServiceManager=Blueprint;name=
BookServiceDao
Provide-Capability = 
osgi.service;effective:=active;objectClass=javax.persistence.EntityManagerFactory;osgi.unit.name=test,
osgi.service;effective:=active;objectClass=org.apache.aries.jpa.template.JpaTemplate;osgi.unit.name=test,
osgi.service;effective:=active;objectClass=javax.persistence.EntityManager;osgi.unit.name=test,
osgi.service;effective:=active;objectClass=org.apache.aries.jpa.supplier.EmSupplier;osgi.unit.name=test
Require-Capability = 
osgi.service;effective:=active;javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl;objectClass=javax.persistence.spi.PersistenceProvider,
osgi.extender;osgi.extender=aries.jpa,
 osgi.service;effective:=active;filter:=(osgi.jndi.service.name=jdbc/test);objectClass=javax.sql.DataSource,
osgi.ee;filter:=(&(osgi.ee=JavaSE)(version=1.5))

Export-Package = 
com.javatechnics.jpa;uses:="com.javatechnics.jpa.dao,javax.persistence";version=1.0.0,
com.javatechnics.jpa.dao;uses:=com.javatechnics.jpa;version=1.0.0
Import-Package = 
com.javatechnics.jpa,
com.javatechnics.jpa.dao,
javax.persistence;version="[1.1,2)",
org.osgi.service.blueprint;version="[1.0.0,2.0.0)"
Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
D-Dᴙum
  • 7,689
  • 8
  • 58
  • 97
  • What is the error message when it fails? The `effective:=active` requirement should be ignored by the OSGi Resolver. – Neil Bartlett Dec 12 '17 at 20:04
  • I'm also not keen on the idea that you hack the requirements of a bundle -- which are real requirements! -- in order to satisfy some installation platform. Better to find a way to satisfy those requirements on the platform. – Neil Bartlett Dec 12 '17 at 20:05
  • @NeilBartlett Included the error and further details. Yes I admit it is a dirty hack, but would like to keep the PU and DS in separate bundles but have a single install feature if possible. – D-Dᴙum Dec 12 '17 at 22:11

1 Answers1

0

If you know that a bundle, let’s call it xyz, provides the PersistenceProvider service then you can write one additional bundle that simply does this:

Require-Bundle: xyz; bundle-version="[...)"
Provide-Capability: osgi.service;
    objectClass=javax.persistence.spi.PersistenceProvider;
    javax.persistence.provider=org.apache.openjpa.persistence.PersistenceProviderImpl;
    effective:=active

This essentially augments bundle xyz with a capability that will resolve the requirement in your bundle, at the cost of adding an otherwise useless bundle.

This is still something of a workaround but is better than removing a real requirement from a bundle.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • I am an OSGi noob. I have a 3rd party bundle for Apache Ant with `osgi.ee; (&(osgi.ee=JavaSE)(version=1.8))`, but actually the bundle would also work on Java 9+, which is why I would like to override to `>=1.8`. Ant itself runs with the latest Java versions, only the plugin is too restrictive. Unfortunately, trying to override this required capability yields error `A bundle is not allowed to define a capability in the osgi.ee name space`. Any ideas how to proceed here while waiting for the upstream bundle to be fixed? Sorry for hijacking your answer. – kriegaex Apr 15 '21 at 07:18
  • @kriegaex There is no need to override the requirement. It requires *at least* Java 1.8. That means it will resolve and run on versions above 1.8. – Neil Bartlett Apr 16 '21 at 08:35
  • No, it does not. I did not invent the quoted error message, which I am trying to avoid. – kriegaex Apr 16 '21 at 10:19
  • You said that the error message came when you tried to override the required capability. So, don't override it and the error message should go away. – Neil Bartlett Apr 16 '21 at 11:05
  • To be a little clearer: any bundle can have a *requirement* on the `osgi.ee` namespace. The Apache Ant bundle has a requirement for `osgi.ee` of JavaSE-1.8, which means it will resolve and run on Java-1.8 and above. Bundles are not allowed to add a *capability* in the `osgi.ee` namespace, because only the OSGi Framework itself is allowed to do that... it is based on the actual version of Java that the Framework has worked out at runtime. – Neil Bartlett Apr 16 '21 at 11:07
  • Maybe we misunderstand each other here. If I do **not** override, the build complains about that very requirement not being fulfilled on Java 9+. On Java 8 it is fulfilled, but then I get the problems with the bundles containing Java 11 class files. So your statement that `version=1.8` actually means the same as `version>=1.8` does not seem to be quite correct. I would be happy if you were right, though, because then I would not have the problem and not even have felt compelled to try and override it in the first place. – kriegaex Apr 16 '21 at 16:42
  • The requirement for `(&(osgi.ee=JavaSE)(version=1.8))` definitely does mean "at least 1.8", per the OSGi specification. Actually the way it works is that Java 1.8 offer the capability of Java 1.8 and 1.7 and 1.6 and so on all the way down. Java 11 offers the capability of Java 11 and 10 and 9 and 1.8 and so on. I don't know what build tool you are using but it should absolutely know this. Perhaps you should open a new StackOverflow question with all the details of how you are building etc. – Neil Bartlett Apr 19 '21 at 08:09
  • OK, thanks for this clarification. I am building with Maven, using an upstream project, not having created the POMs by myself. The Maven build uses Eclipse Equinox stuff. I think at this point I agree that a new question would be better. I am just trying to figure out how to isolate the problem into an [MCVE](https://stackoverflow.com/help/mcve) which is easy for everyone to run and reproduce. Thanks for now. – kriegaex Apr 19 '21 at 08:42