Skip to content

TOMEE-4650 - Guard undeploy against a closed EMF and add JPA 3.2 CDI qualifier beans#2847

Open
jungm wants to merge 1 commit into
mainfrom
claude/tomee-4650-fix-26d0cd
Open

TOMEE-4650 - Guard undeploy against a closed EMF and add JPA 3.2 CDI qualifier beans#2847
jungm wants to merge 1 commit into
mainfrom
claude/tomee-4650-fix-26d0cd

Conversation

@jungm

@jungm jungm commented Jul 23, 2026

Copy link
Copy Markdown
Member

Fixes TOMEE-4650. Two independent problems, both reproducing on Plume (EclipseLink) and the webprofile distribution (OpenJPA) — so neither is a persistence-provider defect.

1. Undeploy closes an already-closed EntityManagerFactory

When a test (or application) closes a container-managed EntityManagerFactory itself, TomEE's undeploy path called close() on it again, and Assembler.destroyApplication then failed with "Attempting to execute an operation on a closed EntityManagerFactory". The test method passed; only the undeploy step errored.

ReloadableEntityManagerFactory.close() now checks isOpen() before delegating to the underlying EMF.

2. Missing Jakarta Persistence 3.2 CDI qualifier beans

TomEE did not register the CDI beans that the Jakarta EE 11 / Persistence 3.2 CDI integration requires for persistence.xml-declared units. Injecting a qualified EntityManagerFactory, EntityManager, or PersistenceUnitUtil (e.g. @Inject @CtsEm2Qualifier) failed at deploy time with UnsatisfiedResolutionException.

A new JpaCDIExtension registers, for each persistence unit:

  • EntityManagerFactory@ApplicationScoped, bean name = persistence-unit name
  • EntityManager — in the scope given by <scope>, defaulting to jakarta.transaction.TransactionScoped
  • CriteriaBuilder, PersistenceUnitUtil, Cache, SchemaManager, Metamodel@Dependent, each obtained from the matching getter of the EMF

All of them carry the qualifiers declared by the <qualifier> elements, or @Default when none is declared. The bean-registration contract (scopes, qualifiers, bean name) was checked against the Jakarta EE 11 Platform spec (CDI-JPA) and the Persistence 3.2 schema rather than from memory.

Supporting plumbing:

  • <qualifier>/<scope> added to the persistence.xml JAXB model (PersistenceUnit) in the XSD-mandated position. This model is hand-maintained, not generated (the xjc profile does not cover persistence schemas), and TomEE does not validate persistence.xml against a bundled XSD, so no schema files needed updating.
  • Fields carried through PersistenceUnitInfo and AppInfoBuilder, which also honours the jakarta.persistence.qualifiers / jakarta.persistence.scope override properties per the spec.
  • Extension registered in OptimizedLoaderService, alongside the analogous ConcurrencyCDIExtension it is modeled on.

Tests

  • ReloadableEntityManagerFactoryCloseTest — verifies a second close() is a no-op. Confirmed it fails against the unpatched code (2 close calls instead of 1).
  • JpaCDIExtensionTest — verifies qualified and @Default EMF/PersistenceUnitUtil injection across two persistence units. Confirmed it fails against the unpatched code with the exact UnsatisfiedResolutionException from the issue.

Notes for reviewers

  • The excluded TCK tests named in the issue (entityManagerFactoryCloseExceptions/ClientP{m,u}servletTest, ee.cdi.ServletEMLookupTest) live in the separate apache/tomee-tck harness repo; their exclusion lines still need removing there once this merges. That change is not in this PR.
  • SchemaManager/Metamodel/CriteriaBuilder/Cache beans are registered per spec but only EntityManagerFactory and PersistenceUnitUtil are exercised in the unit tests — the others eagerly trigger OpenJPA entity enhancement, which the unenhanced test setup in this module cannot satisfy. The full validation is a TCK run.

🤖 Generated with Claude Code

…qualifier beans

Two independent problems, both reproducing on Plume (EclipseLink) and the
webprofile distribution (OpenJPA), so neither is a provider defect.

1. When an application closes a container-managed EntityManagerFactory itself,
   TomEE's undeploy path closed it again, and Assembler.destroyApplication then
   failed with "Attempting to execute an operation on a closed
   EntityManagerFactory". ReloadableEntityManagerFactory.close() now checks
   isOpen() before delegating.

2. TomEE did not register the CDI beans required by the Jakarta Persistence 3.2 /
   Jakarta EE 11 CDI integration for persistence.xml-declared units, so injecting
   a qualified EntityManagerFactory / EntityManager / PersistenceUnitUtil failed
   with UnsatisfiedResolutionException. A new JpaCDIExtension registers, per
   persistence unit, an @ApplicationScoped EntityManagerFactory (bean name = PU
   name), an EntityManager in the <scope> element's scope (default
   TransactionScoped), and @dependent CriteriaBuilder, PersistenceUnitUtil, Cache,
   SchemaManager and Metamodel beans, all carrying the <qualifier> elements or
   @default when none is declared. Supporting this, <qualifier>/<scope> are added
   to the persistence.xml JAXB model (PersistenceUnit), to PersistenceUnitInfo,
   and copied through AppInfoBuilder, which also honours the
   jakarta.persistence.qualifiers / jakarta.persistence.scope override properties.

Bean-registration contract verified against the Jakarta EE 11 Platform spec
(CDI-JPA) and the Persistence 3.2 schema rather than from memory.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant