Markus Jung created TOMEE-4661:
----------------------------------
Summary: Register the Jakarta Persistence 3.2 CDI beans for
persistence.xml-declared units
Key: TOMEE-4661
URL: https://issues.apache.org/jira/browse/TOMEE-4661
Project: TomEE
Issue Type: Bug
Reporter: Markus Jung
TomEE does not register the CDI beans that the Jakarta EE 11 Platform
specification requires for {{persistence.xml}}-declared persistence units. When
an application injects {{EntityManagerFactory}}, {{EntityManager}}, or
{{PersistenceUnitUtil}} with a qualifier such as {{@CtsEm2Qualifier}},
deployment fails with {{UnsatisfiedResolutionException}}.
This reproduces on Plume (EclipseLink) and on the webprofile distribution
(OpenJPA) alike, so it is not a persistence provider defect.
Split out of TOMEE-4650, which now covers only the undeploy/double-close half.
h2. Required beans
Per the Jakarta EE 11 Platform specification, section "Jakarta Persistence &
Jakarta Context and Dependency Injection (CDI) Integration" ({{CDI-JPA.adoc}}),
the container must make available, for each persistence unit:
* {{EntityManagerFactory}} — scope {{ApplicationScoped}}, bean name = the
persistence unit name
* {{EntityManager}} — scope given by the {{<scope>}} element, defaulting to
{{jakarta.transaction.TransactionScoped}}
* {{CriteriaBuilder}}, {{PersistenceUnitUtil}}, {{Cache}}, {{SchemaManager}},
{{Metamodel}} — scope {{Dependent}}, each obtained by calling the matching
getter on the {{EntityManagerFactory}} bean
All carry the qualifiers given by the {{<qualifier>}} elements of
{{persistence.xml}}, or {{jakarta.enterprise.inject.Default}} if none is
declared. None have interceptor bindings.
Supporting work: {{<qualifier>}} and {{<scope>}} must be added to the
{{persistence.xml}} JAXB model
({{org.apache.openejb.jee.jpa.unit.PersistenceUnit}}, hand-maintained, elements
go after {{provider}} per {{persistence_3_2.xsd}}), carried through
{{PersistenceUnitInfo}} and {{AppInfoBuilder}}.
h2. Steps to reproduce / TCK reference
{{ee.jakarta.tck.persistence.ee.cdi.ServletEMLookupTest}} — excluded in
{{runner-webprofile/exclusions/persistence-servlet.txt}} in the
apache/tomee-tck harness repo. Remove that line once fixed.
Note the TCK's {{persistence.xml}} uses the plain {{<qualifier>}} element, not
the {{cdi:}}-namespaced extension-point form.
h2. Prior attempt and review findings
A first implementation was proposed in
https://github.com/apache/tomee/pull/2847 and dropped from that PR after
review. It is preserved on branch {{claude/tomee-4650-jpa-cdi-extension}}.
Issues to address before it can land:
_Blocking_
* No {{getBeans()}} guard before registering {{@Default}} beans for unqualified
units. Beans added via {{AfterBeanDiscovery.addBean()}} are ordinary enabled
beans, not built-ins, so this produces an {{AmbiguousResolutionException}}
against the common {{@Produces EntityManager}} pattern. Confirmed: it breaks
{{ProducedExtendedEmTest}} and would break {{ResourceLocalCdiEmTest}}.
{{ConcurrencyCDIExtension.registerDefaultBeanIfMissing}} is the model to follow.
* No app/webapp scoping: no dedup of qualifier sets and no filter on
{{unitInfo.webappName}}, so duplicate {{@Default}} beans are registered for two
unqualified units in one app, and in an EAR a webapp receives beans for its
siblings' units. Compare {{ConcurrencyCDIExtension.isVisibleInCurrentApp}}.
* The annotation proxy violates the {{Annotation}} equals/hashCode contract
({{equals}} ignores member values; {{hashCode}} is not 0 for a marker
annotation), so a qualifier with members can select the wrong unit. Reuse
{{ConcurrencyCDIExtension.annotationEquals}}/{{annotationHashCode}} rather than
reimplementing.
* Qualifier validation stops at the {{@Qualifier}} check; it must also reject
members lacking defaults and members lacking {{@Nonbinding}}, as
{{ConcurrencyCDIExtension.validateAndCreateQualifiers}} does.
_Also_
* {{jakarta.persistence.qualifiers}} / {{jakarta.persistence.scope}} are not
specification properties — they appear in the Platform spec's property table
but are not declared in {{jakarta.persistence-api-3.2.0.jar}} and are absent
from {{PersistenceConfiguration}}. Use an {{openejb.*}} prefix instead of
minting names under {{jakarta.*}}.
* {{<scope>}} is not validated as an actual CDI scope, unlike {{<qualifier>}}.
* Use {{JndiConstants.PERSISTENCE_UNIT_NAMING_CONTEXT}} rather than a hardcoded
prefix.
* The {{jakarta.transaction}}-absent fallback is unreachable (openejb-core
depends on it unconditionally) and can only cause a silent scope change; drop
it.
* The {{SchemaManager}} bean can only inject null as wired; fix or drop it.
* The {{EntityManager}} is handed out unwrapped by {{JtaEntityManager}},
bypassing TomEE's JTA integration.
* {{resolveEntityManagerScope}} never consults {{unitInfo.transactionType}}, so
a RESOURCE\_LOCAL unit gets {{TransactionScoped}} and is unusable outside a JTA
transaction. The Platform spec states the default unconditionally, with no
RESOURCE\_LOCAL carve-out; this looks like a spec gap worth raising upstream
rather than guessing.
* The {{@Dependent}} utility beans capture the current delegate at
instantiation, so after {{ReloadableEntityManagerFactory.reload()}} they can be
bound to a superseded EMF. Resolve through the EMF per call, or document the
limitation.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)