[ 
https://issues.apache.org/jira/browse/TOMEE-4650?focusedWorklogId=1032356&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1032356
 ]

ASF GitHub Bot logged work on TOMEE-4650:
-----------------------------------------

                Author: ASF GitHub Bot
            Created on: 27/Jul/26 08:13
            Start Date: 27/Jul/26 08:13
    Worklog Time Spent: 10m 
      Work Description: rzo1 commented on PR #2847:
URL: https://github.com/apache/tomee/pull/2847#issuecomment-5088905089

   Could you split this? There are two unrelated changes here and they're in 
very different states.
   
   **Part 1 — the `ReloadableEntityManagerFactory.close()` guard — I'd merge 
today.** It's small,
   correct, uses the non-lazy `delegate` field rather than `delegate()` so it 
doesn't force
   initialisation, and the test genuinely fails without it. That's the part 
that fixes the reported
   undeploy noise.
   
   **Part 2 — `JpaCDIExtension` — needs rework.** It's modelled on 
`ConcurrencyCDIExtension`, which is
   the right reference, but several of the guards that make that class safe 
were dropped in the copy.
   
   Blocking:
   
   - For every PU without `<qualifier>`, `validateAndCreateQualifiers` returns 
`{@Any, @Default}` and
     `registerBeans` adds `EntityManagerFactory` and `EntityManager` beans with 
those qualifiers, with
     no `getBeans()` check. Beans added via `AfterBeanDiscovery.addBean()` are 
ordinary enabled beans,
     not built-ins, so nothing prefers an application producer over them — 
that's an
     `AmbiguousResolutionException` against the `@Produces EntityManager` 
pattern, which is about as
     common as CDI/JPA patterns get.
   
     Two tests in this very module should now fail deployment: 
`ProducedExtendedEmTest`
     (`EntityManagerProducer.produceEm()` + `@Inject EntityManager` in `A`, PU 
`cdi-em-extended`) and
     `ResourceLocalCdiEmTest` (`EMFProducer.em()` + `@Inject EntityManager` in 
`PersistManager`, PU
     `rl-unit`). Neither is `@Ignore`d, and the extension does run in them —
     `OptimizedLoaderService.loadExtensions` adds it unconditionally at :130 
and `OpenEJBLifecycle`
     sets `CURRENT_APP_INFO` at :190 before `deployer.deploy()`.
   
     `ConcurrencyCDIExtension.registerDefaultBeanIfMissing` (:359) is exactly 
the guard that's missing —
     it takes the `BeanManager` and skips when `beanManager.getBeans(type, 
Default.Literal.INSTANCE)`
     is non-empty. `OpenEJBLifecycle.addInternalBeans` (:244-258) uses the same 
idiom.
   
   - The loop `for (final PersistenceUnitInfo unitInfo : 
appInfo.persistenceUnits)` has no dedup of
     qualifier sets and no filter on `unitInfo.webappName`. So two unqualified 
PUs in one app register
     duplicate `@Default` beans, and in an EAR a webapp gets beans for its 
siblings' PUs —
     `TomcatWebAppBuilder:1454` sets `CURRENT_APP_INFO` to the whole EAR's 
`AppInfo` in the per-webapp
     `!webAppAlone` branch, while the same method correctly filters on 
`unitInfo.webappName` at :1425
     for EMF creation. `ConcurrencyCDIExtension` scopes this with 
`isVisibleInCurrentApp(resource, currentAppIds)`
     (:102); there's no equivalent here.
   
   - The annotation proxy breaks the `Annotation` equals/hashCode contract: 
`equals` is
     `annotationType.isInstance(args[0])` and `hashCode` is 
`annotationType.hashCode()`. The spec
     mandates 0 for a marker annotation, and `equals` ignoring member values 
makes it asymmetric with a
     real annotation instance — so a qualifier with members can select the 
wrong PU. The correct
     implementation is ~150 lines away in `ConcurrencyCDIExtension` 
(`annotationEquals` :235,
     `annotationHashCode` :254, `annotationToString`) and was replaced here by 
two one-liners. Please
     reuse it rather than reimplementing.
   
     Related omission: `ConcurrencyCDIExtension.validateAndCreateQualifiers` 
(:184-195) rejects
     qualifiers with members lacking defaults and members lacking 
`@Nonbinding`, via two
     `addDefinitionError` calls. `JpaCDIExtension.validateAndCreateQualifiers` 
stops at the `@Qualifier`
     check. So `@Qualifier @interface Unit { String value(); }` gives 
`getDefaultValue() == null`, the
     proxy returns null from `value()`, and you get either an NPE inside OWB or 
a bean that can never
     be matched — with no diagnostic. The javadoc on `createAnnotation` asserts 
"the CDI qualifier
     rules guarantee [a default] to exist"; that guarantee doesn't exist.
   
   Also:
   
   - `jakarta.persistence.qualifiers` and `jakarta.persistence.scope` aren't 
spec properties. I unpacked
     `jakarta.persistence-api-3.2.0.jar` — neither string appears anywhere in 
the jar, and
     `PersistenceConfiguration` declares constants for every standard override 
property (JDBC_*,
     LOCK_TIMEOUT, QUERY_TIMEOUT, SCHEMAGEN_*, VALIDATION_*, CACHE_MODE) with 
nothing for these two.
     `persistence_3_2.xsd` defines only the `<qualifier>`/`<scope>` elements. 
Please don't mint new
     property names under the `jakarta.*` namespace — `openejb.*` is the right 
prefix for a
     TomEE-specific carrier.
   
   - The `SchemaManager` bean can only ever inject null: `addUtilityBean(..., 
SchemaManager.class, EntityManagerFactory::getSchemaManager)`
     on the reloadable EMF. Either wire it properly or drop it.
   
   - The `jakarta.transaction`-absent fallback is unreachable — openejb-core 
has a hard dependency on it
     (`cdi/transactional/TransactionContext` imports 
`TransactionManager`/`TransactionScoped` directly
     and extends `AbstractContext(TransactionScoped.class)`); the module can't 
load without it. Its only
     possible effect is a silent scope change, and the javadoc describes 
reflective member access that
     isn't happening.
   
   - `<scope>` isn't validated as an actual CDI scope, unlike the `<qualifier>` 
right above it. A typo
     there fails much later and much less clearly.
   
   - The JNDI prefix is hardcoded rather than using 
`JndiConstants.PERSISTENCE_UNIT_NAMING_CONTEXT`.
   
   - `qualifierSelectsTheMatchingPersistenceUnit` is tautological — it would 
pass against a stub.
   
   Question rather than a finding: `produceWith(instance -> 
lookupEntityManagerFactory(unitInfo.id).createEntityManager())`
   hands out the provider EM unwrapped by `JtaEntityManager`, so it bypasses 
TomEE's JTA integration —
   and `resolveEntityManagerScope` never consults `unitInfo.transactionType` 
(populated at
   `AppInfoBuilder:685`), so a RESOURCE_LOCAL unit gets `TransactionScoped`. 
`TransactionContext.isActive()`
   (:50-60) returns false with no JTA transaction, so that EM is unusable 
outside one. Is
   `TransactionScoped` mandated unconditionally by the platform spec here, with 
apps expected to declare
   `<scope>` for RESOURCE_LOCAL? The 3.2 XSD (:117) documents `<scope>` with no 
stated default, so I
   couldn't settle it from the schema alone.
   
   Low-priority, only reachable via the JPA JMX operations: the `@Dependent`
   `CriteriaBuilder`/`Metamodel`/`Cache`/`PersistenceUnitUtil` beans call the 
accessor once at
   instantiation, so they capture the *current* delegate. 
`ReloadableEntityManagerFactory.reload()`
   (:383-389) replaces the delegate without closing the old one, so after a 
reload an
   `@ApplicationScoped` bean holds a `CriteriaBuilder` bound to the superseded 
EMF while
   `@Inject EntityManagerFactory` follows the new one. Resolving through the 
EMF per call, or a
   documented limitation, would avoid the inconsistency.
   




Issue Time Tracking
-------------------

    Worklog Id:     (was: 1032356)
    Time Spent: 20m  (was: 10m)

> Undeploy closes an already-closed EntityManagerFactory; PU CDI qualifier 
> beans missing
> --------------------------------------------------------------------------------------
>
>                 Key: TOMEE-4650
>                 URL: https://issues.apache.org/jira/browse/TOMEE-4650
>             Project: TomEE
>          Issue Type: Bug
>            Reporter: Markus Jung
>            Assignee: Markus Jung
>            Priority: Major
>          Time Spent: 20m
>  Remaining Estimate: 0h
>
> When a test closes a container-managed {{EntityManagerFactory}} (EMF) itself, 
> TomEE's undeploy path calls {{close()}} on it again. 
> {{Assembler.destroyApplication}} then fails with "Attempting to execute an 
> operation on a closed EntityManagerFactory". The test method itself passes; 
> only the undeploy step after it errors. TomEE must check whether the EMF is 
> already closed before calling {{close()}} on it during undeploy.
> Separately, TomEE does not register the CDI qualifier beans that Jakarta 
> Persistence 3.2 requires for {{persistence.xml}}-declared units. When an app 
> injects {{EntityManagerFactory}}, {{EntityManager}}, or 
> {{PersistenceUnitUtil}} with a qualifier such as {{@CtsEm2Qualifier}}, 
> deployment fails with {{UnsatisfiedResolutionException}}. TomEE is missing 
> this part of the Jakarta Persistence 3.2 CDI integration.
> Both problems show up on Plume (EclipseLink) and on the webprofile 
> distribution (OpenJPA) alike, so neither is a persistence provider defect.
> h2. Steps to reproduce / TCK reference
> * 
> {{ee.jakarta.tck.persistence.core.entityManagerFactoryCloseExceptions.ClientPmservletTest}}
>  and {{ClientPuservletTest}} — excluded in 
> {{runner-webprofile/exclusions/persistence-javatest.txt}} in the 
> apache/tomee-tck harness repo. The {{exceptionsTest}} methods pass; the class 
> reports an undeploy error.
> * {{ee.jakarta.tck.persistence.ee.cdi.ServletEMLookupTest}} — excluded in 
> {{runner-webprofile/exclusions/persistence-servlet.txt}}. Deployment fails 
> with {{UnsatisfiedResolutionException}} for {{@CtsEm2Qualifier}}.
> Remove the matching lines from both exclusion files once fixed.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to