XARecoveryModule runs scans against XAResource and holds the results. On the next invocation it compares the new and old scan results to determine which xids are still outstanding, assumes they are in need of recovery and deals with them. This two stage process is to avoid rolling back tx branches that are still live and just between the prepare/[commit|rollback] phases. The idea, whilst good in principle, is flawed in execution.
It operates on the assumption that consecutive scans against a given RM will use the same (as defined by hashcode/equals semantics) XAResource. This assumption fails where a different connection to the same RM is used and the driver is implemented such that the XAResource identity is tied to the [XA]Connection. This occurs particularly with AppServerJDBCXARecovery, which uses a fresh connection on each pass to ensure that recovery scans don't blow up because of stale connections, such as occur when a db bounces but the AS keeps running. More generally, the contract between XARecoveryModule, the XAResourceRecovery impls and XAResourceRecoveryHelper impls needs to be flexible enough to accommodate this.
The upshot of this is that XARecoveryModule can't assume a given RM will be represented by the same XAResource on different passes and must therefore cache Xids between passes using a different structure. It's actually not required to hold the XAResource itself between passes, since the resource for the current pass will always be available. Thus keeping only the Xids from the previous scan would seem best.