rzo1 commented on PR #2877:
URL: https://github.com/apache/tomee/pull/2877#issuecomment-5318833333

   Currently short on time, so AI review only: I ran an adversarial review pass 
over this PR, followed by a second pass whose job was to *refute* the first 
one's findings against the actual code. Everything below survived that second 
pass. Take it as input, not as a verdict — I have not run the build.
   
   **Overall: the mechanism looks sound.** `addServiceLoaderProviders` is wired 
into `configureFactory` after the `application.getClasses()` provider loop, so 
the `alreadyRegistered` check de-dupes against both the `Class` entries and the 
singleton instances `RESTService` puts into `additionalProviders`. The local 
`ArrayList` copy means the caller's collection is not mutated. The classloader 
story holds: `configureFactory` runs with the TCCL set to 
`CxfUtil.initBusLoader()` and `CxfContainerClassLoader.findResources` delegates 
to the per-thread webapp loader, so webapp descriptors are visible. 
`InternalApplication.getProperties()` delegates to the original application, so 
the `jakarta.ws.rs.loadServices` opt-out is not lost by the wrapper. The 
hand-rolled `hasNext()/next()` error loop was reproduced standalone against JDK 
17 `ServiceLoader` — it recovers from missing classes, throwing constructors 
and missing no-arg constructors, and terminates in every case.
   
   ### 1. `@ConstrainedTo(CLIENT)` features are rejected with an exception 
instead of being skipped (minor)
   
   `CxfRsHttpListener.java` — the discovered instances are appended to 
`additionalProviders` and therefore flow into `providers(...)`, whose instance 
branch calls `isNotServerProvider(o.getClass())`. That method does not merely 
filter: with `openejb.jaxrs.fail-on-constrainedto` defaulting to `true` it 
*throws* `IllegalArgumentException`.
   
   That check was previously only ever applied to providers the application 
itself declared, i.e. things the developer opted into. Service loading changes 
the population being validated: it now includes every `Feature` declared by any 
jar visible to the webapp classloader. `@ConstrainedTo(RuntimeType.CLIENT)` is 
precisely the mechanism the spec gives a library to say "do not register me on 
the server", so the container should skip it, not fail.
   
   Scope check, so this is not overstated: the exception escapes 
`configureFactory` → `deployApplication` → 
`RESTService.afterApplicationCreated`, but 
`ObserverManager.MethodInvocation.invoke` catches `InvocationTargetException`, 
fires `ObserverFailed` and logs SEVERE without rethrowing — so 
`Assembler.createApplication` continues and the application still deploys. What 
is lost is the REST publication of that app (and of any webapp later in the 
loop), with a SEVERE stack trace. Also, no artifact in a full local `~/.m2` 
scan ships a `META-INF/services/jakarta.ws.rs.core.Feature` descriptor at all 
today, so this is a forward-looking robustness gap rather than an observable 
regression.
   
   Suggested fix: filter `@ConstrainedTo` inside `addServiceLoaderProviders` 
and drop the entry with a warning, or pass a flag so `isNotServerProvider` 
never throws for service-loaded entries. `FAIL_ON_CONSTRAINED_TO` exists to 
catch mistakes in the app's own provider set; it should not judge ambient 
classpath entries.
   
   ### 2. `jakarta.ws.rs.loadServices` is only honoured as a `Boolean`, not as 
`"false"` (minor)
   
   `isServiceLoadingEnabled()` uses 
`!Boolean.FALSE.equals(properties.get(LOAD_SERVICES_PROPERTY))`. 
`Application.getProperties()` is user-supplied and commonly carries values that 
originated as strings (config files, init-params, property files merged into 
the map). `"false"` is not `Boolean.FALSE`, so the opt-out is silently ignored 
and the scan still runs. Accepting `Boolean.FALSE` or `"false"` 
(case-insensitive) would make the spec switch behave as users expect.
   
   ### 3. The comment and test name overstate the per-entry recovery (nit)
   
   The comment ("a single broken entry must not discard the remaining providers 
of this type") and `brokenEntryDoesNotDiscardTheOthers` promise more than the 
JDK allows. Per-entry recovery works for unresolvable class names, missing 
no-arg constructors and throwing constructors — all three verified. But a 
malformed *line* makes `ServiceLoader.parse()` abort the whole file (it reads 
the descriptor eagerly and fails it entirely, dropping even entries listed 
before the bad line), and since `configs` has already consumed that URL the 
loop resumes at the next descriptor. The loop is still safe and terminates — 
this is purely a wording issue. Either soften the comment or add the 
malformed-line case to the test so the real limit is documented.
   
   ### Test coverage
   
   All eight new tests call 
`addServiceLoaderProviders`/`isServiceLoadingEnabled` in isolation. Nothing 
exercises the new `if (!ignoreAutoProviders && 
isServiceLoadingEnabled(application))` wiring in `configureFactory`, nor the 
`providers(...)` path the discovered instances actually take — which is where 
finding 1 lives. A test declaring a `@ConstrainedTo(RuntimeType.CLIENT)` 
feature in a descriptor and asserting the deployment still publishes would pin 
that invariant.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to