Context
I have a large OSGi-based (Equinox 3.9/Eclipse RCP 4.4) application, with a few "optional" bundles. Each of these optional bundles provide alternative user interfaces and some extra services (through SCR components) that are applicable only to a subset of our customers.
The application is always distributed as a pre-packaged installation (that is, we do not use P2 nor any other automatic provisionning mecanism). Until recently, we built a different pre-packaged installation for each customer that needed some optional bundles. The number of optional bundles is now growing up, and so does the number of custom pre-packaged installations to be built.
So we would like to build only one installation package to be delivered to all customers, containing all optional bundles, and then decide at runtime which optional bundles shall be launched. But here's the twist: the set of optional features to be enabled is known only after the user has authenticated himself with the server. That is, some end-users have access to multiple accounts, each giving access to a distinct set of optional features to be enabled. Consequently, optional bundles must remains unavailable until the user log-in, and then, only allowed optional bundles should be loaded. Since these optional bundles make contributions through various extenders (Eclipse's Plugin registry, SCR and Blueprints), it means that optional bundles must not be allowed to reach the RESOLVED state until it has been determined that they are indeed authorized to launch. It must also be possible to load and unload those optional bundles, as the user log-in and log-out of a specific server account.
Potential solutions and questions
I have identified a few potential solutions, but have unresolved questions to each of them. So I only really need proper positive answers to all questions of any of the following scenario.
Have optional bundles require some "enabling" capability. That would be, I think, the cleanest approach. Optional bundles would add something like "Require-capability: com.acme.optionalfeatures; identifier=my-custom-feature-identifier". At runtime, the capability would remains unresolved until the user is authentified, so extenders would automaticlay ignore that bundle. The bundle would automaticaly change state once the enabling capability is registered. That's great. But here are the missing parts: a) How can I register new capabilities namespace? b) Can I dynamically alter a bundle's provided capability, and if so, how can I do so? c) (if (b) is not possible) Can I dynamically register a new "Resource" with new capabilities, and if so, how can I do so?
Have optional bundles import some "enabling" package. That is a variant on the previous scenario, which might be easier to manage than custom capability namespaces... Those "enabling packages" could then come into existance through "dynamicaly created" packages. That is, some manager bundle would call BundleContext#installBundle(String, InputStream), with a ByteArrayInputStream, returning a dynamically generated bundle archive containing a Manifest.MF which export appropriate "enabling packages". Sounds simple enough, but several components along the toolchain (the IDE, PDE, P2, Product Export...) will complain about the fact that these required packages doesn't exist. To avoid those issues, the require-package header would need to be added dynamically at the time is is installed into the framework. But is there any mechanism that allow a bundle's headers to be altered that early (that is as soon as the bundle is installed into the framework)? Class Weaving is not applicable here.
Have optional bundles all require a single enabling package (all the same), which indeed exists. Then a Resolve Hook would filter out the bundle exporting that package from potential wiring candidates when the optional bundle has not yet been authorized. But how can I later request that all bundles be reconsidered for resolving when the user log-in or log-out?
Excluded solutions
The following solutions have already been excluded:
Putting optional bundles in distinct directories, then use BundleContext#installBundle(...) to enable thse bundles. Though this solution does indeed work at deploy time, it impose a significant burden on development (because bundles are then at some hard-to-predict folder, relative to the workspace, local git configuration, test environment and so one, making it impossible to properly build the location to be provided to #installBundle). The packaging procedure also become much more complex, as we need first to move apart those optional bundles, and then, to update several Equinox/P2 configuration files to prevent them to locate these now missing bundles.
Prevent optional bundles from being activated by conditionaly throwing an exception form their activator. This would simply not solve our problem, since option bundles would still be allowed to reach the RESOLVED state, and therefore be able to make contributions by the intermediate of extenders.
Use P2 to install new features at runtime. The problem here is that changes to the list of bundles made through P2 1) are inherently persistant (which means that optional features will automatically be reanabled at the next launch) and 2) require a framwork restart to be properly accomplished.
Note: We have no "security"-related concerns about the fact that those extra bundles are actualy distributed to users that do not require them. I know that users might easily hack the installation in order to force the launch of some optional bundles. This pose no significant issue.