TOMEE-4655 - roll back a failed deployment's registered ids - #2850
Conversation
When a CDI or EJB deployment failed, Assembler.createApplication rethrew ValidationException/DeploymentException without undeploying the partially deployed application. CDI startup failures surface as a jakarta.enterprise.inject.spi.DeploymentException, so they took this path and left every already-registered BeanContext in the ContainerSystem. The next application reusing one of those deployment ids then failed with a DuplicateDeploymentIdException before reaching its own checks, turning one bad deployment into a cascade of unrelated failures. That catch clause only ever existed so these two exception types would not be wrapped in an OpenEJBException; skipping the rollback was an unintended side effect. Run destroyApplication(appInfo) on this path as well, then rethrow the original exception unchanged. Adds FailedDeploymentIdCleanupTest, which fails against the previous code with the exact "leaked its deployment id" symptom and passes with the fix.
|
Correct diagnosis and the minimal fix. Confirmed the chain: the unwrapped One thing I'd like fixed before merge, which my local run surfaced:
And one I couldn't prove but would want smoke-tested:
Nit: |
The rollback added for TOMEE-4655 runs over BeanContexts that startEjbs never deployed into their container, because a CDI bootstrap failure happens before startEjbs. EjbJarBuilder assigns the container at build time while the container data only appears in Container.deploy, so the stop/undeploy loops hit a null Data and threw a NullPointerException for every singleton and stateful bean. Those were collected into the UndeployException and logged, burying the real cause under a wall of errors on what is now the most common path through this branch. Guard the two containers that lacked the check that ManagedContainer, StatelessInstanceManager and SingletonInstanceManager.undeploy already have, and skip a bean whose container was already cleared. Also tighten the test to catch DeploymentException rather than Exception, so it keeps guarding the branch it was written for, and add a case that undeploys a bean which was never deployed.
|
Thanks for the careful review — both points confirmed, and both are addressed in e4e60f8. 1. ERROR-level NPE noise on the pre- Reproduced exactly as you described. The root cause is an asymmetry: Notably A third variant showed up once those were guarded: 2. Webapp smoke test — done, and it is not a regression. Deployed a war with an unsatisfied
The normalized SEVERE lines are identical between the two; only their ordering shifts, since the rollback now runs slightly earlier relative to 3. Nit — fixed. The test now catches One thing to flag: 🤖 Addressed by Claude Code |
What
Fixes TOMEE-4655: a failed CDI/EJB deployment leaks its deployment id into later apps.
Why
Assembler.createApplication(AppInfo, ClassLoader, boolean)registers every EJB's deployment id in theContainerSystem(viainitEjbs) before it starts CDI. Itscatchblock treated two exception types specially:CDI startup failures bubble up as
jakarta.enterprise.inject.spi.DeploymentException, so they hit the first clause and returned without undeploying the partially deployed application. EveryBeanContextregistered before the CDI phase stayed inCoreContainerSystem.deployments. The next app reusing one of those ids then trippedgetDuplicates→DuplicateDeploymentIdExceptionbefore any of its own lifecycle/managed-bean/concurrency checks ran — turning one bad deployment into a cascade of unrelated failures.The git history shows that clause was only ever added so these two exception types wouldn't be wrapped in an
OpenEJBException(commit51ab12b2, "as ValidationException, DeploymentException shouldn't be wrapped"). Losing the rollback was an accidental side effect, not the intent.The fix
Run
destroyApplication(appInfo)on this path as well, then rethrow the original exception unchanged — preserving the "don't wrap" intent while releasing the ids.Testing
New
FailedDeploymentIdCleanupTestdeploys an app whose singleton has an unsatisfiable@Inject(failing at CDI start), then asserts a second app can reuse the same deployment id. Verified it has real diagnostic power:AssertionError: the failed deployment leaked its deployment id expected null, but was:<BeanContext(id=TheSharedDeploymentId)>assembler.classictests (RedeployTest,EjbRefTest, etc.)Also verified end-to-end against the Jakarta EE 11 Web Profile TCK (
enterprise-beans-30partition, Plume): 1138 tests, 0 failures, 0 errors, and zeroDuplicateDeploymentIdException. The four excluded test patterns the ticket calls out now deploy cleanly.Note for reviewers
The matching TCK-harness changes (removing the exclusions, updating the expected class count, and
KNOWN_FAILURES.md) live in the separateapache/tomee-tckrepo and will be submitted there. TwoJsfClientEjblitejsfTestclasses among those exclusions still fail after this fix, but for an unrelated, already-documented reason — the OpenWebBeans "cannot proxy a final method" interceptor gap, which the deployment-id leak had been masking. They are moved into that existing exclusion block rather than left enabled.🤖 Generated with Claude Code