I've looked into this in some more detail, and have tracked things down a little.
Each plan contains two bundles, one that is specific to the plan (BundleTwoForPlan and BundleTwoTwoForPlan) and one that is 'shared' (BundleOneForPlan). The problem lies with BundleOneForPlan. Even though the plan is scoped (so two copies of the bundle should be installed) the deployer stages the bundle to the same location for each plan. This means that the same install location is passed to Equinox for each install of BundleOneForPlan and the second install is no-oped (as per the OSGi spec). This means that we end up with MyPlanTwo trying to use BundleOneForPlan from MyPlan which falls apart as the manifest scoping is all wrong. This is the root cause of the problem.
There's also a secondary issue that we won't see once that first problem is fixed, but we should address it nevertheless:
Everything resolves successfully in the deployer's side state (as the install mechanism is different) but then fails to resolve in the global state. However we fail to notice this failure. I've tracked this down to the fact that we ignore the return value from PackageAdmin.resolveBundles(). This then breaks a precondition of start processing which is that we expect all bundles that the deployer is trying to start to be resolved. We need to check this return value.
To summarise, the change that is required to fix this issue:
- Staging needs to become scope aware so that we get multiple copies of a bundle when its referenced in multiple scoped plans
And the change that's required to fix the secondary issue:
- ResolveStage.process() needs to check the return value from PackageAdmin.resolveBundles and throw a DeploymentException if it's false (ideally containing details of the resolution failure).