In an ideal world we'd be able to use an interceptor-chain kind of pattern where each processor calls invokeNext() or whatever, and any error cleanup could be done in a finally block. However the grim reality is that we could face stack exhaustion if we had a truly large number of extensions involved. I think the next-best option is what you suggest: a cleanup() method which is called if a later processor fails.
So to restate:
- Add a cleanup() method to DeploymentUnitProcessor
- Each implementation of DUP should handle failure cleanly, so processDeployment is essentially atomic: either everything gets done or nothing (no partial state when an exception is thrown).
- Then the implementation of cleanup() can perform a full cleanup safely, because it will only ever be called if processDeployment() had completed successfully and a later processDeployment() failed.
- This implies that if a given processDeployment() fails, its cleanup() is not called, but the cleanup() methods on all the DUPs before the one that failed are called.