OSGi / Spifly Provider for jakarta.ws.rs.ext.RuntimeDelegate cannot be found
I have an OSGI environment with jersey-common 3.1.9, jakarta.ws.rs 3.1.0 and spifly 1.3.7
On startup console says
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Provider for jakarta.ws.rs.ext.RuntimeDelegate cannot be found
at jakarta.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:118)
at jakarta.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:88)
at jakarta.ws.rs.core.Response$ResponseBuilder.newInstance(Response.java:776)
at jakarta.ws.rs.core.Response.status(Response.java:522)
at jakarta.ws.rs.core.Response.status(Response.java:533)
at jakarta.ws.rs.core.Response.ok(Response.java:566)
at ch.elexis.core.services.es.NoRemoteEventService.<init>(NoRemoteEventService.java:8)
at ch.elexis.core.services.es.ElexisServerService.<init>(ElexisServerService.java:72)
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
... 146 more
Caused by: java.lang.ClassNotFoundException: Provider for jakarta.ws.rs.ext.RuntimeDelegate cannot be found
at jakarta.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:163)
at jakarta.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:103)
... 154 more
jakarta.ws.rs.ext.RuntimeDelegate is available within jersey-common and the /META-INF/services/jakarta.ws.rs.ext.RuntimeDelegate file exists.
It is, however, NOT being picked up by spifly. This is due to /META-INF/MANIFEST.MF not containing the required Require-Capability and Provide-Capability entries. See https://aries.apache.org/documentation/modules/spi-fly.html#specconf for details.
For example:
logback-classic correctly writes this info to MANIFEST-MF in https://github.com/qos-ch/logback/blob/5610c96b4d705a4fe6ded9c42d4f47cb92bbbd95/logback-classic/pom.xml#L343
while jersey-common does not do so in https://github.com/eclipse-ee4j/jersey/blob/d9658aa3064236abf1280f16e1705454e5d0b599/core-common/pom.xml#L150C25-L150C120
So adding the resp. extension should make OSGI Spifly pick up the required RuntimeDelegate and make it run, while it should not change behaviour for non-osgi uses of the bundle.
jakarta.ws.rs should be 3.1.0. Please check if it's not mistyped in your report or in the app (which is the worst case).
jakarta.ws.rsshould be3.1.0. Please check if it's not mistyped in your report or in the app (which is the worst case).
Corrected, it is in fact jakarta.ws.rs-api 3.1.0
Thanks for the correction. Regarding the issue itself, Jersey already runs within at least a Glassfish OSGi environment, and there are no issues with it. We have never tried it with Spifly. It is probably possible to add those entries to comply with Spifly. This could be considered an enhancement for future releases.
It might well be the case that it works in other OSGi environments, but I wonder on how the SPI implementation is detected. It did work with jakarta.ws.rs-api 2.1.6 where javax.ws.rs.ext.FactoryFinder seems to be implemented differently. All services, that get picked up correctly by SpiFly have the OSGi compatbiel annotation, another example is jakarta.xml.bind-api.jar - have a look at https://github.com/jakartaee/jaxb-api/blob/748c50bb8f71b4687febca5400fa9c574644aac3/api/pom.xml#L237
Do you know how Glassfish uses the ServiceLoader within its OSGi environment, because the problems should be the same?!
At the moment I am puzzled on how to fix this - as it would involve some weird classloading workarounds.
At the moment I am puzzled on how to fix this - as it would involve some weird classloading workarounds.
What comes to my mind is just forking the Jersey repo, fixing it in the branch, and using a snapshot instead. If you then provide a PR to the Jersey repo (requires signing of ECA), it would make the fix available in the nearest version of Jersey. Otherwise, we could fix that later.
I made some progress here, and found out that every medal has two sites - lets sum it up
- Spifly does dynamically modify java.util.Serviceloader
- To have spifly intervene either bundle taking place in the process must opt-in for consuming and/or providing
So in my scenario, the following patch will provide mark the services to spifly
https://github.com/col-panic/jersey/commit/0a5b19a316da2ab0c80f976d88b1fc5673fbfd04
being jakarta.ws.rs.client.ClientBuilder in core-client and jakarta.ws.rs.ext.RuntimeDelegate and org.glassfish.jersey.internal.spi.AutoDiscoverable in core-common
BUT jakarta.ws.rs-api has to opt-in to spifly to consuming these services (if a single class is not mentioned, spifly will not inject it), which is done via a patch to another repostiory
https://github.com/col-panic/jax-ws-api/commit/4e3cdba5d5b1a0cf3a9ec0d583d2fe1d891b0817#diff-e55bdf06100eef1c252327789d9233a9f5fc308dc7f6b5e60caccaa6c3c44477R146 REF https://github.com/jakartaee/jax-ws-api/issues/227
JAXB as Example
jakarta.xml.bind-api does set the Require-Capability in https://github.com/jakartaee/jaxb-api/blob/748c50bb8f71b4687febca5400fa9c574644aac3/api/pom.xml#L238
and jakarta jaxb-osgi does satisfy this in https://github.com/eclipse-ee4j/jaxb-ri/blob/b7d1ff7a13cecfaadde733387216b5cad09cc5b5/jaxb-ri/bundles/osgi/osgi/pom.xml#L295
This project simply seem to not yet have picked this up
For my test scenario the capabilities I mention here do suffice to make it run! I am sure, however, that for a proper patch we would have to expose all services that are somehow concered by jakarta.ws.rs.ext.FactoryFinder.
@col-panic - thanks for providing those patches and related info. Regarding the issue you are submitting to the jax-ws-api issue tracker - that is wrong API. You should submit the issue to the jakartaee/rest - that is the API Jersey implements.
thanks @senivam for your feedback, this stuff keeps constantly confusing me, because somehow the patch there fixed the problem for me.
So to set this straight
- https://github.com/jakartaee/jax-ws-api =
jakarta.xml.ws-apii.e. JAX-WS(Java API for XML Web Services) - https://github.com/jakartaee/rest =
jakarta.ws.rs-apii.e. JAX-RS (Java API for RESTful Web Services) - https://github.com/jakartaee/jaxb-api =
jakarta.xml.bind-apii.e. JAXB (Java XML Binding)
I see, somehow i mixed up my remotes - i will fix all of this and update the patches - might take some days....
yes, those URLs and descriptions you are providing:
- https://github.com/jakartaee/jax-ws-api =
jakarta.xml.ws-apii.e. JAX-WS(Java API for XML Web Services)- https://github.com/jakartaee/rest =
jakarta.ws.rs-apii.e. JAX-RS (Java API for RESTful Web Services)
are correct.
@senivam To help you understand the interconnection I did a little demo on the behaviour of JAXB in https://github.com/col-panic/os-development-spifly-demo - please have a look at it, it should make things clearer.
Just found out, that the same problem exists for jakarta.websocket-api see https://stackoverflow.com/questions/39740531/jetty-websocket-java-lang-runtimeexception-cannot-load-platform-configurator where as a solution the same approache is provided.
The respective project is https://github.com/jakartaee/websocket
Luckily the implementation of the respective Configurator by Jetty 12 already has the required META-INF/MANIFEST.MF entries after build - see https://github.com/jetty/jetty.project/blob/61f773643ee366a52097d02a486696496560f820/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml#L58
Current findings: The following code suffices, and removing the resolution:=optional will make manually starting the bundle unnecessary.
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<instructions>
<Import-Package>
${jakarta.annotation.osgi.version},
*
</Import-Package>
<unpackBundle>true</unpackBundle>
<!-- Needed to integrate ServiceLoader mechanism with OSGi -->
<!-- see https://docs.osgi.org/specification/osgi.enterprise/7.0.0/service.loader.html#d0e61377 -->
<Require-Capability><![CDATA[
1 osgi.extender;filter:="(&(osgi.extender=osgi.serviceloader.registrar)(version>=1.0.0)(!(version>=2.0.0)))",
2 osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))"
]]></Require-Capability>
<Provide-Capability><![CDATA[
3 osgi.serviceloader;osgi.serviceloader="jakarta.ws.rs.client.ClientBuilder"
]]></Provide-Capability>
</instructions>
</configuration>
</plugin>
Here the explanation for the single lines (see https://docs.osgi.org/specification/osgi.enterprise/7.0.0/service.loader.html#d0e61377 for general info)
- (1) Opt-In for processing this bundle, see https://docs.osgi.org/specification/osgi.enterprise/7.0.0/service.loader.html#d0e61120
- (2) If no version is supplied, the plugin will insert the java version used to compile - thus manual setting
- (3) https://docs.osgi.org/specification/osgi.enterprise/7.0.0/service.loader.html#i3285744
@col-panic It would have been so great if you could provide a simple HelloWorld application that fails without the changes provided in the PR and pass with them. But I suppose it would not work before the API is modified, correct?
@jansupol did you take a look at https://github.com/col-panic/os-development-spifly-demo ?
I'd have to fix an example based on this one to show the required functionality. Not sure on how to provide both a working patch an the original - maybe I find a way.
I am nearly finished upgrading our app, and have to make adapted versions of
jakarta.ws.rs-apijakarta.websocket-apijersey-clientjersey-common
to get them running with spifly.
I did notify all respective projects but as mentioned in https://github.com/jakartaee/websocket/issues/469#issuecomment-2677858361 there seems to be an inconsistency in the different jakarta ee projects in whether to add the meta information proposed here or not ...