Repository navigation
fix: Throw the EntityManager's own exception from the kernel DAOs' proxy - EXO-91299 - #1355
Conversation
…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)
Backport evidenceSource: #1344, squash-merged into Picks: Adaptations: none. The pick applied cleanly, and no commit was added. Verification
Level: N2, restated from the source. A backport from |
|



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
EntityManagerHolderproxy invoked the thread's EntityManager by reflection without unwrapping theInvocationTargetException, which the JDK proxy then wrapped in anUndeclaredThrowableException: a catch of a JPA exception, anIllegalArgumentExceptionor anIllegalStateExceptiondoes not recognise it.Fix: the proxy rethrows the exception the EntityManager threw.
EntityManagerHolderTestpins 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
GenericDAOJPAImplsubclass, 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 thanExceptionorRuntimeExceptionbehaves differently for an exception:IllegalArgumentExceptionorIllegalStateException, now fires. Hibernate has by then marked the transaction rollback-only for most of them (not forNoResultException,NonUniqueResultExceptionand the lock and query timeouts): inside an outer@ExoTransactionalmethod, 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;UndeclaredThrowableExceptionaround a kernel DAO call no longer matches, and anErrorthe EntityManager throws now passes a catch ofExceptionorRuntimeException, as anErrordoes 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
UndeclaredThrowableExceptionaround 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