[ 
https://issues.apache.org/jira/browse/GROOVY-12690?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King updated GROOVY-12690:
-------------------------------
    Description: 
h2. Background

Groovy's optional modules are published as OSGi fragments of the core 
{{groovy}} bundle ({{Fragment-Host: groovy}}, 
GROOVY-8666/GROOVY-9402/GROOVY-11570), and the core bundle carries 
{{Eclipse-ExtensibleAPI: true}} for PDE (GROOVY-8713). Any module that ships a 
non-AST {{META-INF/services}} file also gets these ServiceLoader Mediator 
headers (GROOVY-8766, restored in GROOVY-10881):

{code}
Require-Capability: 
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)",
 osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)",
 
osgi.serviceloader;filter:="(osgi.serviceloader=<service>)";cardinality:=multiple
Provide-Capability: osgi.serviceloader;osgi.serviceloader="<service>"
{code}

The two {{osgi.extender}} requirements are *mandatory*. Only a ServiceLoader 
Mediator implementation (in practice Apache Aries SPI Fly) provides them. In a 
framework without SPI Fly these modules never resolve, so the fragment never 
attaches and its packages are not visible at all. This currently affects 
groovy-json, groovy-reactor, groovy-rxjava, groovy-test-junit5, 
groovy-test-junit6, groovy-testng, groovy-contracts and groovy-jsr223 (and also 
groovy-grape-ivy and groovy-grape-maven, although Grape's runtime dependency 
resolution is not meaningful inside an OSGi container).

Historically, Groovy's OSGi users have fallen into two broad camps:
* Felix-based containers (Karaf, Sling/AEM), which typically install SPI Fly 
(GROOVY-10881, GROOVY-11578).
* Equinox / Eclipse RCP (GROOVY-8666, GROOVY-8713).

Fixes for one camp have repeatedly broken the other. {{groovy-osgi-test}} 
(GROOVY-11898) was meant to break that cycle. However, it only covers the core 
bundle on Felix, and its two groovy-json tests are {{@Disabled}} (citing the 
unrelated GROOVY-5092). They fail because the test framework has no SPI Fly, so 
the fragment stays {{INSTALLED}}.

h2. Investigation

An embedded-framework probe (plain {{FrameworkFactory}} via {{ServiceLoader}}) 
installed the Groovy jars plus manifest-only consumer bundles. The consumers 
used either {{Import-Package: groovy.json}} or {{Require-Bundle: groovy}} and 
called {{JsonSlurper.parseText}}. The core bundle and consumers were started, 
as a real container would start them. The matrix covered:
* *Frameworks:* Felix 7.0.5, Felix 8.0.0 and Equinox ({{org.eclipse.osgi}} 
3.24.300).
* *SPI Fly:* none, 1.2.4, or 1.3.8 ({{org.apache.aries.spifly.dynamic.bundle}} 
+ ASM).
* *Manifests:* as built, or with the extender requirements marked 
{{resolution:=optional}}.
* *Fallback:* {{FastStringUtils}} with its GROOVY-11578 default-service 
fallback, and a scratch build with the fallback removed.
* *Thread context class loader (TCCL):* left as is, or set to the consumer 
bundle's class loader.

h3. groovy-json, 7.0.0-SNAPSHOT, SPI Fly 1.3.8

On Felix, the (/) results apply to {{Import-Package}} consumers only (see 
"Other findings").

||groovy-json variant||Felix, no SPI Fly||Felix + SPI Fly||Equinox, no SPI 
Fly||Equinox + SPI Fly||
|mandatory requirements, fallback (current)|(x) fragment does not 
resolve|(/)|(x) fragment does not resolve|(/)|
|mandatory requirements, no fallback|(x) fragment does not resolve|(/)|(x) 
fragment does not resolve|(/)|
|optional requirements, fallback|(/)|(/)|(/)|(/)|
|optional requirements, no fallback|(x) {{Unable to load 
FastStringService}}|(/)|(!) works only via Equinox's {{ContextFinder}} TCCL; 
fails if the caller sets its own TCCL|(/)|

h3. groovy-reactor (core-side consumer: {{AwaitableAdapterRegistry}} uses its 
own class loader)

||groovy-reactor variant||No SPI Fly (Felix and Equinox)||With SPI Fly (Felix 
and Equinox)||
|mandatory requirements (current)|(x) fragment does not resolve; only built-in 
adapters|(/) {{ReactorAwaitableAdapter}} discovered|
|optional requirements|(/) {{ReactorAwaitableAdapter}} discovered|(/)|

h3. Historical check: Groovy 4.0.26 (pre-fallback) from Maven Central

||SPI Fly||Felix 7.0.5||Felix 8.0.0||Equinox||
|1.2.4|(x) weaving hook fails: {{Unsupported class file major version 61}}|(x) 
same|-|
|1.3.8|(/)|(/)|(/)|

h3. Conclusions

# *SPI Fly mediation works for fragment-provided services.* With SPI Fly 1.3.x 
and the host bundle started, groovy-json works even without the fallback, on 
both Felix and Equinox. The Felix version (7.0.5 vs 8.0.0) makes no difference.
# *Without SPI Fly, groovy-json only works because of the GROOVY-11578 
fallback.* {{FastStringUtils}} calls {{ServiceLoader.load(Class)}}, which uses 
the TCCL. Equinox's default {{ContextFinder}} TCCL masks this; an explicit TCCL 
does not.
# *Core-side consumers do not need SPI Fly at all.* 
{{AwaitableAdapterRegistry}} passes its own class loader, and a fragment's 
{{META-INF/services}} is on the host class loader. For groovy-reactor, the 
mandatory requirement only stops the module from resolving.
# *Marking the extender requirements optional loses nothing.* Every cell that 
works with mandatory requirements still works with optional ones, including SPI 
Fly mediation. The previously failing no-SPI-Fly cells start working.
# *SPI Fly 1.2.4 is not viable on JDK 17+.* Its weaving hook fails even for 
Groovy 4.0.26, which is Java 8 bytecode. The likely cause is that its embedded 
ASM cannot read the JDK 17 runtime classes it inspects while weaving. So the 
GROOVY-11578 workaround of downgrading SPI Fly no longer applies; SPI Fly 1.3.x 
is required.
# *The race described in GROOVY-11578 reproduces.* With the host resolved but 
not started, a consumer using JSON before SPI Fly has registered the provider 
fails without the fallback, and succeeds with it.

h3. Other findings (not addressed here)

* *Felix does not expose fragment-contributed exports to {{Require-Bundle}} 
consumers.* Equinox does. This reproduces with a minimal Groovy-free 
host/fragment/consumer, so it is framework behaviour rather than a Groovy 
manifest problem. On Felix, {{Import-Package}} works.
* *{{Eclipse-ExtensibleAPI}} cannot be verified by a runtime test.* It is a PDE 
compile-time hint that Equinox ignores at runtime.

h2. Proposed changes

h3. 1. Make the ServiceLoader Mediator requirements optional

In {{build-logic/src/main/groovy/org.apache.groovy-library.gradle}}:

{code}
instruction 'Require-Capability', 
'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional'
instruction 'Require-Capability', 
'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional'
...
instruction 'Require-Capability', 
"osgi.serviceloader;filter:=\"(osgi.serviceloader=${it.name})\";cardinality:=multiple;resolution:=optional"
{code}

{{Provide-Capability}} is unchanged.
* Where SPI Fly is installed, the optional requirements are still wired at 
resolve time and mediation still happens.
* Where it is not, the modules now resolve. Services consumed via an explicit 
class loader work, and groovy-json works through its fallback.

h3. 2. Extend groovy-osgi-test

* *Equinox:* add a {{testEquinox}} task that runs the same tests with Felix 
swapped for Equinox on the classpath.
* *SPI Fly as an optional configuration:* add an {{osgiSpifly}} configuration, 
not on the test classpath, containing SPI Fly 1.3.x and ASM (reusing Groovy's 
ASM version). Its jars are passed to the tests, which run each scenario both 
with and without SPI Fly installed.
* *Replace the two {{@Disabled}} tests* with consumer-bundle tests 
({{Import-Package}}, and {{Require-Bundle}} on Equinox) that exercise:
** *groovy-json resolution and use* ({{JsonSlurper.parseText}}, 
{{JsonOutput.toJson}}), with and without SPI Fly.
** *groovy-reactor's {{AwaitableAdapter}} discovery*, a core-side ServiceLoader 
consumer, with and without SPI Fly.
** *SPI Fly mediation itself:* a small test-only fragment provides its own 
{{FastStringServiceFactory}}. {{FastStringUtils}} prefers a non-default 
service, so the test can assert that provider was used. That passes only if 
mediation actually happened, which the fallback would otherwise hide.
** *The GROOVY-11578 race:* JSON used while the host is resolved but not yet 
started.
* *Record the Felix {{Require-Bundle}} limitation* as a documented expected 
behaviour rather than a failure.
* *Pin versions* (SPI Fly 1.3.8, Equinox 3.24.300) and update 
{{gradle/verification-metadata.xml}}.

  was:
h2. Background

Groovy's optional modules are published as OSGi fragments of the core 
{{groovy}} bundle ({{Fragment-Host: groovy}}, 
GROOVY-8666/GROOVY-9402/GROOVY-11570), and the core bundle carries 
{{Eclipse-ExtensibleAPI: true}} for PDE (GROOVY-8713). Any module that ships a 
non-AST {{META-INF/services}} file also gets these ServiceLoader Mediator 
headers (GROOVY-8766, restored in GROOVY-10881):

{code}
Require-Capability: 
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)",
 osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)",
 
osgi.serviceloader;filter:="(osgi.serviceloader=<service>)";cardinality:=multiple
Provide-Capability: osgi.serviceloader;osgi.serviceloader="<service>"
{code}

The two {{osgi.extender}} requirements are *mandatory*. Only a ServiceLoader 
Mediator implementation (in practice Apache Aries SPI Fly) provides them. In a 
framework without SPI Fly these modules never resolve, so the fragment never 
attaches and its packages are not visible at all. This currently affects 
groovy-json, groovy-reactor, groovy-rxjava, groovy-test-junit5, 
groovy-test-junit6, groovy-testng, groovy-grape-ivy, groovy-grape-maven, 
groovy-contracts and groovy-jsr223.

Historically, Groovy's OSGi users have fallen into two broad camps:
* Felix-based containers (Karaf, Sling/AEM), which typically install SPI Fly 
(GROOVY-10881, GROOVY-11578).
* Equinox / Eclipse RCP (GROOVY-8666, GROOVY-8713).

Fixes for one camp have repeatedly broken the other. {{groovy-osgi-test}} 
(GROOVY-11898) was meant to break that cycle. However, it only covers the core 
bundle on Felix, and its two groovy-json tests are {{@Disabled}} (citing the 
unrelated GROOVY-5092). They fail because the test framework has no SPI Fly, so 
the fragment stays {{INSTALLED}}.

h2. Investigation

An embedded-framework probe (plain {{FrameworkFactory}} via {{ServiceLoader}}) 
installed the Groovy jars plus manifest-only consumer bundles. The consumers 
used either {{Import-Package: groovy.json}} or {{Require-Bundle: groovy}} and 
called {{JsonSlurper.parseText}}. The core bundle and consumers were started, 
as a real container would start them. The matrix covered:
* *Frameworks:* Felix 7.0.5, Felix 8.0.0 and Equinox ({{org.eclipse.osgi}} 
3.24.300).
* *SPI Fly:* none, 1.2.4, or 1.3.8 ({{org.apache.aries.spifly.dynamic.bundle}} 
+ ASM).
* *Manifests:* as built, or with the extender requirements marked 
{{resolution:=optional}}.
* *Fallback:* {{FastStringUtils}} with its GROOVY-11578 default-service 
fallback, and a scratch build with the fallback removed.
* *Thread context class loader (TCCL):* left as is, or set to the consumer 
bundle's class loader.

h3. groovy-json, 7.0.0-SNAPSHOT, SPI Fly 1.3.8

On Felix, the (/) results apply to {{Import-Package}} consumers only (see 
"Other findings").

||groovy-json variant||Felix, no SPI Fly||Felix + SPI Fly||Equinox, no SPI 
Fly||Equinox + SPI Fly||
|mandatory requirements, fallback (current)|(x) fragment does not 
resolve|(/)|(x) fragment does not resolve|(/)|
|mandatory requirements, no fallback|(x) fragment does not resolve|(/)|(x) 
fragment does not resolve|(/)|
|optional requirements, fallback|(/)|(/)|(/)|(/)|
|optional requirements, no fallback|(x) {{Unable to load 
FastStringService}}|(/)|(!) works only via Equinox's {{ContextFinder}} TCCL; 
fails if the caller sets its own TCCL|(/)|

h3. groovy-reactor (core-side consumer: {{AwaitableAdapterRegistry}} uses its 
own class loader)

||groovy-reactor variant||No SPI Fly (Felix and Equinox)||With SPI Fly (Felix 
and Equinox)||
|mandatory requirements (current)|(x) fragment does not resolve; only built-in 
adapters|(/) {{ReactorAwaitableAdapter}} discovered|
|optional requirements|(/) {{ReactorAwaitableAdapter}} discovered|(/)|

h3. Historical check: Groovy 4.0.26 (pre-fallback) from Maven Central

||SPI Fly||Felix 7.0.5||Felix 8.0.0||Equinox||
|1.2.4|(x) weaving hook fails: {{Unsupported class file major version 61}}|(x) 
same|-|
|1.3.8|(/)|(/)|(/)|

h3. Conclusions

# *SPI Fly mediation works for fragment-provided services.* With SPI Fly 1.3.x 
and the host bundle started, groovy-json works even without the fallback, on 
both Felix and Equinox. The Felix version (7.0.5 vs 8.0.0) makes no difference.
# *Without SPI Fly, groovy-json only works because of the GROOVY-11578 
fallback.* {{FastStringUtils}} calls {{ServiceLoader.load(Class)}}, which uses 
the TCCL. Equinox's default {{ContextFinder}} TCCL masks this; an explicit TCCL 
does not.
# *Core-side consumers do not need SPI Fly at all.* 
{{AwaitableAdapterRegistry}} passes its own class loader, and a fragment's 
{{META-INF/services}} is on the host class loader. For groovy-reactor, the 
mandatory requirement only stops the module from resolving.
# *Marking the extender requirements optional loses nothing.* Every cell that 
works with mandatory requirements still works with optional ones, including SPI 
Fly mediation. The previously failing no-SPI-Fly cells start working.
# *SPI Fly 1.2.4 is not viable on JDK 17+.* Its weaving hook fails even for 
Groovy 4.0.26, which is Java 8 bytecode. The likely cause is that its embedded 
ASM cannot read the JDK 17 runtime classes it inspects while weaving. So the 
GROOVY-11578 workaround of downgrading SPI Fly no longer applies; SPI Fly 1.3.x 
is required.
# *The race described in GROOVY-11578 reproduces.* With the host resolved but 
not started, a consumer using JSON before SPI Fly has registered the provider 
fails without the fallback, and succeeds with it.

h3. Other findings (not addressed here)

* *Felix does not expose fragment-contributed exports to {{Require-Bundle}} 
consumers.* Equinox does. This reproduces with a minimal Groovy-free 
host/fragment/consumer, so it is framework behaviour rather than a Groovy 
manifest problem. On Felix, {{Import-Package}} works.
* *{{Eclipse-ExtensibleAPI}} cannot be verified by a runtime test.* It is a PDE 
compile-time hint that Equinox ignores at runtime.

h2. Proposed changes

h3. 1. Make the ServiceLoader Mediator requirements optional

In {{build-logic/src/main/groovy/org.apache.groovy-library.gradle}}:

{code}
instruction 'Require-Capability', 
'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional'
instruction 'Require-Capability', 
'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional'
...
instruction 'Require-Capability', 
"osgi.serviceloader;filter:=\"(osgi.serviceloader=${it.name})\";cardinality:=multiple;resolution:=optional"
{code}

{{Provide-Capability}} is unchanged.
* Where SPI Fly is installed, the optional requirements are still wired at 
resolve time and mediation still happens.
* Where it is not, the modules now resolve. Services consumed via an explicit 
class loader work, and groovy-json works through its fallback.

h3. 2. Extend groovy-osgi-test

* *Equinox:* add a {{testEquinox}} task that runs the same tests with Felix 
swapped for Equinox on the classpath.
* *SPI Fly as an optional configuration:* add an {{osgiSpifly}} configuration, 
not on the test classpath, containing SPI Fly 1.3.x and ASM (reusing Groovy's 
ASM version). Its jars are passed to the tests, which run each scenario both 
with and without SPI Fly installed.
* *Replace the two {{@Disabled}} tests* with consumer-bundle tests 
({{Import-Package}}, and {{Require-Bundle}} on Equinox) that exercise:
** *groovy-json resolution and use* ({{JsonSlurper.parseText}}, 
{{JsonOutput.toJson}}), with and without SPI Fly.
** *groovy-reactor's {{AwaitableAdapter}} discovery*, a core-side ServiceLoader 
consumer, with and without SPI Fly.
** *SPI Fly mediation itself:* a small test-only fragment provides its own 
{{FastStringServiceFactory}}. {{FastStringUtils}} prefers a non-default 
service, so the test can assert that provider was used. That passes only if 
mediation actually happened, which the fallback would otherwise hide.
** *The GROOVY-11578 race:* JSON used while the host is resolved but not yet 
started.
* *Record the Felix {{Require-Bundle}} limitation* as a documented expected 
behaviour rather than a failure.
* *Pin versions* (SPI Fly 1.3.8, Equinox 3.24.300) and update 
{{gradle/verification-metadata.xml}}.


> Make the ServiceLoader-mediator requirements in module OSGi manifests 
> optional, and extend groovy-osgi-test to cover Equinox and SPI Fly
> ----------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12690
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12690
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>
> h2. Background
> Groovy's optional modules are published as OSGi fragments of the core 
> {{groovy}} bundle ({{Fragment-Host: groovy}}, 
> GROOVY-8666/GROOVY-9402/GROOVY-11570), and the core bundle carries 
> {{Eclipse-ExtensibleAPI: true}} for PDE (GROOVY-8713). Any module that ships 
> a non-AST {{META-INF/services}} file also gets these ServiceLoader Mediator 
> headers (GROOVY-8766, restored in GROOVY-10881):
> {code}
> Require-Capability: 
> osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)",
>  osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)",
>  
> osgi.serviceloader;filter:="(osgi.serviceloader=<service>)";cardinality:=multiple
> Provide-Capability: osgi.serviceloader;osgi.serviceloader="<service>"
> {code}
> The two {{osgi.extender}} requirements are *mandatory*. Only a ServiceLoader 
> Mediator implementation (in practice Apache Aries SPI Fly) provides them. In 
> a framework without SPI Fly these modules never resolve, so the fragment 
> never attaches and its packages are not visible at all. This currently 
> affects groovy-json, groovy-reactor, groovy-rxjava, groovy-test-junit5, 
> groovy-test-junit6, groovy-testng, groovy-contracts and groovy-jsr223 (and 
> also groovy-grape-ivy and groovy-grape-maven, although Grape's runtime 
> dependency resolution is not meaningful inside an OSGi container).
> Historically, Groovy's OSGi users have fallen into two broad camps:
> * Felix-based containers (Karaf, Sling/AEM), which typically install SPI Fly 
> (GROOVY-10881, GROOVY-11578).
> * Equinox / Eclipse RCP (GROOVY-8666, GROOVY-8713).
> Fixes for one camp have repeatedly broken the other. {{groovy-osgi-test}} 
> (GROOVY-11898) was meant to break that cycle. However, it only covers the 
> core bundle on Felix, and its two groovy-json tests are {{@Disabled}} (citing 
> the unrelated GROOVY-5092). They fail because the test framework has no SPI 
> Fly, so the fragment stays {{INSTALLED}}.
> h2. Investigation
> An embedded-framework probe (plain {{FrameworkFactory}} via 
> {{ServiceLoader}}) installed the Groovy jars plus manifest-only consumer 
> bundles. The consumers used either {{Import-Package: groovy.json}} or 
> {{Require-Bundle: groovy}} and called {{JsonSlurper.parseText}}. The core 
> bundle and consumers were started, as a real container would start them. The 
> matrix covered:
> * *Frameworks:* Felix 7.0.5, Felix 8.0.0 and Equinox ({{org.eclipse.osgi}} 
> 3.24.300).
> * *SPI Fly:* none, 1.2.4, or 1.3.8 
> ({{org.apache.aries.spifly.dynamic.bundle}} + ASM).
> * *Manifests:* as built, or with the extender requirements marked 
> {{resolution:=optional}}.
> * *Fallback:* {{FastStringUtils}} with its GROOVY-11578 default-service 
> fallback, and a scratch build with the fallback removed.
> * *Thread context class loader (TCCL):* left as is, or set to the consumer 
> bundle's class loader.
> h3. groovy-json, 7.0.0-SNAPSHOT, SPI Fly 1.3.8
> On Felix, the (/) results apply to {{Import-Package}} consumers only (see 
> "Other findings").
> ||groovy-json variant||Felix, no SPI Fly||Felix + SPI Fly||Equinox, no SPI 
> Fly||Equinox + SPI Fly||
> |mandatory requirements, fallback (current)|(x) fragment does not 
> resolve|(/)|(x) fragment does not resolve|(/)|
> |mandatory requirements, no fallback|(x) fragment does not resolve|(/)|(x) 
> fragment does not resolve|(/)|
> |optional requirements, fallback|(/)|(/)|(/)|(/)|
> |optional requirements, no fallback|(x) {{Unable to load 
> FastStringService}}|(/)|(!) works only via Equinox's {{ContextFinder}} TCCL; 
> fails if the caller sets its own TCCL|(/)|
> h3. groovy-reactor (core-side consumer: {{AwaitableAdapterRegistry}} uses its 
> own class loader)
> ||groovy-reactor variant||No SPI Fly (Felix and Equinox)||With SPI Fly (Felix 
> and Equinox)||
> |mandatory requirements (current)|(x) fragment does not resolve; only 
> built-in adapters|(/) {{ReactorAwaitableAdapter}} discovered|
> |optional requirements|(/) {{ReactorAwaitableAdapter}} discovered|(/)|
> h3. Historical check: Groovy 4.0.26 (pre-fallback) from Maven Central
> ||SPI Fly||Felix 7.0.5||Felix 8.0.0||Equinox||
> |1.2.4|(x) weaving hook fails: {{Unsupported class file major version 
> 61}}|(x) same|-|
> |1.3.8|(/)|(/)|(/)|
> h3. Conclusions
> # *SPI Fly mediation works for fragment-provided services.* With SPI Fly 
> 1.3.x and the host bundle started, groovy-json works even without the 
> fallback, on both Felix and Equinox. The Felix version (7.0.5 vs 8.0.0) makes 
> no difference.
> # *Without SPI Fly, groovy-json only works because of the GROOVY-11578 
> fallback.* {{FastStringUtils}} calls {{ServiceLoader.load(Class)}}, which 
> uses the TCCL. Equinox's default {{ContextFinder}} TCCL masks this; an 
> explicit TCCL does not.
> # *Core-side consumers do not need SPI Fly at all.* 
> {{AwaitableAdapterRegistry}} passes its own class loader, and a fragment's 
> {{META-INF/services}} is on the host class loader. For groovy-reactor, the 
> mandatory requirement only stops the module from resolving.
> # *Marking the extender requirements optional loses nothing.* Every cell that 
> works with mandatory requirements still works with optional ones, including 
> SPI Fly mediation. The previously failing no-SPI-Fly cells start working.
> # *SPI Fly 1.2.4 is not viable on JDK 17+.* Its weaving hook fails even for 
> Groovy 4.0.26, which is Java 8 bytecode. The likely cause is that its 
> embedded ASM cannot read the JDK 17 runtime classes it inspects while 
> weaving. So the GROOVY-11578 workaround of downgrading SPI Fly no longer 
> applies; SPI Fly 1.3.x is required.
> # *The race described in GROOVY-11578 reproduces.* With the host resolved but 
> not started, a consumer using JSON before SPI Fly has registered the provider 
> fails without the fallback, and succeeds with it.
> h3. Other findings (not addressed here)
> * *Felix does not expose fragment-contributed exports to {{Require-Bundle}} 
> consumers.* Equinox does. This reproduces with a minimal Groovy-free 
> host/fragment/consumer, so it is framework behaviour rather than a Groovy 
> manifest problem. On Felix, {{Import-Package}} works.
> * *{{Eclipse-ExtensibleAPI}} cannot be verified by a runtime test.* It is a 
> PDE compile-time hint that Equinox ignores at runtime.
> h2. Proposed changes
> h3. 1. Make the ServiceLoader Mediator requirements optional
> In {{build-logic/src/main/groovy/org.apache.groovy-library.gradle}}:
> {code}
> instruction 'Require-Capability', 
> 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional'
> instruction 'Require-Capability', 
> 'osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional'
> ...
> instruction 'Require-Capability', 
> "osgi.serviceloader;filter:=\"(osgi.serviceloader=${it.name})\";cardinality:=multiple;resolution:=optional"
> {code}
> {{Provide-Capability}} is unchanged.
> * Where SPI Fly is installed, the optional requirements are still wired at 
> resolve time and mediation still happens.
> * Where it is not, the modules now resolve. Services consumed via an explicit 
> class loader work, and groovy-json works through its fallback.
> h3. 2. Extend groovy-osgi-test
> * *Equinox:* add a {{testEquinox}} task that runs the same tests with Felix 
> swapped for Equinox on the classpath.
> * *SPI Fly as an optional configuration:* add an {{osgiSpifly}} 
> configuration, not on the test classpath, containing SPI Fly 1.3.x and ASM 
> (reusing Groovy's ASM version). Its jars are passed to the tests, which run 
> each scenario both with and without SPI Fly installed.
> * *Replace the two {{@Disabled}} tests* with consumer-bundle tests 
> ({{Import-Package}}, and {{Require-Bundle}} on Equinox) that exercise:
> ** *groovy-json resolution and use* ({{JsonSlurper.parseText}}, 
> {{JsonOutput.toJson}}), with and without SPI Fly.
> ** *groovy-reactor's {{AwaitableAdapter}} discovery*, a core-side 
> ServiceLoader consumer, with and without SPI Fly.
> ** *SPI Fly mediation itself:* a small test-only fragment provides its own 
> {{FastStringServiceFactory}}. {{FastStringUtils}} prefers a non-default 
> service, so the test can assert that provider was used. That passes only if 
> mediation actually happened, which the fallback would otherwise hide.
> ** *The GROOVY-11578 race:* JSON used while the host is resolved but not yet 
> started.
> * *Record the Felix {{Require-Bundle}} limitation* as a documented expected 
> behaviour rather than a failure.
> * *Pin versions* (SPI Fly 1.3.8, Equinox 3.24.300) and update 
> {{gradle/verification-metadata.xml}}.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to