Skip to content

fix: Throw the EntityManager's own exception from the kernel DAOs' proxy - EXO-91299 - #1355

Merged
boubaker merged 1 commit into
developfrom
backport/EXO-91299-entitymanager-exceptions
Oct 27, 2026
Merged

boubaker merged 1 commit into
developfrom
backport/EXO-91299-entitymanager-exceptions

Conversation

@boubaker

Copy link
Copy Markdown
Member

Symptom: a database error raised inside an EntityManager call of a kernel DAO (find, persist, merge, remove, refresh...) skipped every catch naming that exception. In video calls, such a failure while saving a call, such as reading the call or a participant before writing it, left the service as an UndeclaredThrowableException instead of the service's own call error (a StorageException naming the step, wrapped in an InvalidCallException when starting, joining, leaving, stopping or removing a call), and the code that tolerates a storage error, such as removing a call whose owner no longer exists while reading it, failed the whole read instead. A failure at commit was not affected: the commit does not go through the proxy.

Cause: the EntityManagerHolder proxy invoked the thread's EntityManager by reflection without unwrapping the InvocationTargetException, which the JDK proxy then wrapped in an UndeclaredThrowableException: a catch of a JPA exception, an IllegalArgumentException or an IllegalStateException does not recognise it.

Fix: the proxy rethrows the exception the EntityManager threw. EntityManagerHolderTest pins it on a real EntityManager: a find with a null id, a persist of a detached entity and a refresh of a removed row each reach the DAO's caller as the engine's own exception, and the EntityManager the proxy opened outside a request is closed after a failed call.

Impact: every caller of a kernel DAO (a GenericDAOJPAImpl subclass, in every addon) now receives the exception an EntityManager method called on the proxy throws (find, persist, merge, remove, refresh, flush, getReference...); the execution of a query never went through the proxy and is unchanged. Only a catch narrower than Exception or RuntimeException behaves differently for an exception:

  • a catch of the EntityManager's exception, or of IllegalArgumentException or IllegalStateException, now fires. Hibernate has by then marked the transaction rollback-only for most of them (not for NoResultException, NonUniqueResultException and the lock and query timeouts): inside an outer @ExoTransactional method, such a catch that does not rethrow lets the aspect roll the transaction back without an error, where the failure used to reach the caller;
  • a catch of UndeclaredThrowableException around a kernel DAO call no longer matches, and an Error the EntityManager throws now passes a catch of Exception or RuntimeException, as an Error does anywhere else.

A sweep of the direct callers in the org's repositories found the catches that now fire only in web-conferencing, none of them inside an outer transaction, and no catch of UndeclaredThrowableException around a kernel DAO call. exoplatform/web-conferencing#421 pins the video-call errors on this fix; its build passes once this fix is published.

Knowledge: Meeds-io/eng-standards#560

This change is classified N2 (at ffe62c50f6) — its approver must be an Archi/Dev who knows it is N2, not an approval on AI review alone.

🤖 Generated with Claude Code

…oxy - EXO-91299 (#1344)

**Symptom**: a database error raised inside an EntityManager call of a
kernel DAO (find, persist, merge, remove, refresh...) skipped every
catch naming that exception. In video calls, such a failure while saving
a call, such as reading the call or a participant before writing it,
left the service as an UndeclaredThrowableException instead of the
service's own call error (a StorageException naming the step, wrapped in
an InvalidCallException when starting, joining, leaving, stopping or
removing a call), and the code that tolerates a storage error, such as
removing a call whose owner no longer exists while reading it, failed
the whole read instead. A failure at commit was not affected: the commit
does not go through the proxy.

**Cause**: the `EntityManagerHolder` proxy invoked the thread's
EntityManager by reflection without unwrapping the
`InvocationTargetException`, which the JDK proxy then wrapped in an
`UndeclaredThrowableException`: a catch of a JPA exception, an
`IllegalArgumentException` or an `IllegalStateException` does not
recognise it.

**Fix**: the proxy rethrows the exception the EntityManager threw.
`EntityManagerHolderTest` pins it on a real EntityManager: a find with a
null id, a persist of a detached entity and a refresh of a removed row
each reach the DAO's caller as the engine's own exception, and the
EntityManager the proxy opened outside a request is closed after a
failed call.

**Impact**: every caller of a kernel DAO (a `GenericDAOJPAImpl`
subclass, in every addon) now receives the exception an EntityManager
method called on the proxy throws (find, persist, merge, remove,
refresh, flush, getReference...); the execution of a query never went
through the proxy and is unchanged. Only a catch narrower than
`Exception` or `RuntimeException` behaves differently for an exception:
- a catch of the EntityManager's exception, or of
`IllegalArgumentException` or `IllegalStateException`, now fires.
Hibernate has by then marked the transaction rollback-only for most of
them (not for `NoResultException`, `NonUniqueResultException` and the
lock and query timeouts): inside an outer `@ExoTransactional` method,
such a catch that does not rethrow lets the aspect roll the transaction
back without an error, where the failure used to reach the caller;
- a catch of `UndeclaredThrowableException` around a kernel DAO call no
longer matches, and an `Error` the EntityManager throws now passes a
catch of `Exception` or `RuntimeException`, as an `Error` does anywhere
else.

A sweep of the direct callers in the org's repositories found the
catches that now fire only in web-conferencing, none of them inside an
outer transaction, and no catch of `UndeclaredThrowableException` around
a kernel DAO call. exoplatform/web-conferencing#421 pins the video-call
errors on this fix; its build passes once this fix is published.

Knowledge: Meeds-io/eng-standards#560

This change is classified N2 (at `ffe62c50f6`) — its approver must be an
Archi/Dev who knows it is N2, not an approval on AI review alone.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7b35dfa)
@boubaker

Copy link
Copy Markdown
Member Author

Backport evidence

Source: #1344, squash-merged into feature/maintenance. Its change is on origin/feature/maintenance at 7b35dfa4c2 (matched against the branch's current head, 2026-10-27). The PO validated it on the maintenance acceptance server on 2026-10-27.

Picks: 57125d9f5d = git cherry-pick -x 7b35dfa4c2, on origin/develop (80a006a86a).

Adaptations: none. The pick applied cleanly, and no commit was added.

Verification

  • git show -U0 <pick> | git patch-id --stable equals the same command on 7b35dfa4c2: 798b13decaea.
  • gh pr diff 1344 | git patch-id --stable equals git show 57125d9f5d | git patch-id --stable: 8411dfb75c5d.
  • The source's approval is on its head: APPROVED by ahamdi at ffe62c50f6, which is the source PR's head.
  • mvn -B -ntp -Dcheckstyle.skip=true -pl component/common -am at 57125d9f5d: install: Tests run: 197, Failures: 0, Errors: 0, Skipped: 0, BUILD SUCCESS (the source reported its full reactor, 1653 tests, so the counts are not comparable).
  • Merge order: before the stacked Meeds-io/portal EXO-91427 and EXO-91183 backports, and before exoplatform/web-conferencing's EXO-91299 backport, whose test needs this change published.

Level: N2, restated from the source. A backport from feature/maintenance whose every pick's patch-id equals its source's diff merges on its first approval (ai-review-and-merge.md §3). It lands by squash, with the source's body as its message.

@boubaker
boubaker requested review from Jihed525 and ahamdi October 27, 2026 12:05
@boubaker
boubaker enabled auto-merge (squash) October 27, 2026 12:05
@sonarqubecloud

Copy link
Copy Markdown

@boubaker
boubaker merged commit df7956b into develop Oct 27, 2026
13 checks passed
@boubaker
boubaker deleted the backport/EXO-91299-entitymanager-exceptions branch October 27, 2026 12:18
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.

2 participants