Bundle Start Level [message #667661] |
Sat, 30 April 2011 19:10 |
Terran Gilman Messages: 67 Registered: July 2009 |
Member |
|
|
A project I am currently working on requires heavy use of the OSGi StartLevel service. The functionality is used to provide a level of lifecycle control for components that is separate from the normal lifecycle of the OSGi framework. There are several reasons for this, but primarily the goal is to allow online component maintenance, configuration, and provisioning.
The project consists of a core foundational framework that offers some basic services, such as storage, data source management, and remote software installation. There are control mechanisms that allow transitioning the platform between three virtual states: stopped, maintenance mode, and started. These states are implemented using a specific range of start levels. It is intended that other developers external to our effort will produce components designed to operate within our framework. Developers are encouraged to place all non-foundation components in the started band, which can be considered similar to user space versus kernel space.
Recently we have migrated our platform to the current 3.6 version of equinox and have decided to leverage p2 to perform all of our bundle management functions. This has been challenging but ultimately it has been a considerable improvement over performing all these tasks ourselves. One issue we have not been able to completely close is the assigning of start level values to bundles.
In the beginning of my research I found the eclipse touchpoints for setting the start level and marking the bundle as started. Since we were already using bundle specific touchpoint operations in some bundles, this seemed like a simple and straightforward solution to the problem. However, these touchpoints appeared to not work. I found this forum post discussing this exact behavior:
http://www.eclipse.org/forums/index.php?t=tree&th=163356 &#page_top
In that dialog, it was mentioned that a bug had been submitted for the problem:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=253244
Here is the start level documentation for p2:
http://wiki.eclipse.org/Equinox/p2/Setting_Start_Levels
I'll start by summarizing what I have come to understand after reading the discussion threads and reviewing the available documentation. When a bundle is exported into a p2 repository, a configuration unit (CU) for the bundle is automatically generated and contains instructions for several p2 phases. The instructions included in the CU set the bundle start level to 4 and mark the bundle to be started, among other things. The reason the setStartLevel() and markStarted() touchpoints appear not to work properly when added to a developer provided p2.inf for a bundle is that the local bundle instructions are executed prior to executing the instructions included in the auto-generated CU; resulting in the local bundle start level being overwritten.
The recommended way to assign a start level to a bundle is to manually create a new CU that will replace the auto-generated CU and place this configuration in an existing feature that includes the target bundle or to create a new feature specifically for this purpose. The mechanism used to override the auto-generated CU is based on the fact that p2 selects a single CU from the set of available fragments based on how many host dependencies it declares. Although this approach appears to work, I have some concerns and observations that I'd like to share.
Level of effort in proportion to functional need
There is a significant difference in the amount of work and knowledge of p2 that is required when assigning a start level to a bundle directly compared to performing the same task through a feature. The foundation we are building is intended to be used by third parties. The developers may not be greatly familiar with the eclipse environment, let alone well versed in p2 metadata configuration. It is far simpler to document and train someone to add a single file into their bundles that would contain 1 or 2 lines that are quite easy to read and understand:
instructions.configure= setStartLevel(startLevel:30); \
markStarted(started:true);
The feature configuration is quite different:
#create a requirement on the fragment we are creating
requires.0.namespace=org.eclipse.equinox.p2.iu
requires.0.name=configure.org.example.bundle
requires.0.range=[$version$,$version$]
requires.0.greedy=true
#create a IU fragment named configure.org.example.bundle
units.0.id=configure.org.example.bundle
units.0.version=$version$
units.0.provides.1.namespace=org.eclipse.equinox.p2.iu
units.0.provides.1.name=configure.org.example.bundle
units.0.provides.1.version=$version$
units.0.instructions.install=org.eclipse.equinox.p2.touchpoint.eclipse.installBundle(bundle:${artifact});
units.0.instructions.uninstall=org.eclipse.equinox.p2.touchpoint.eclipse.uninstallBundle(bundle:${artifact});
units.0.instructions.unconfigure=org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel:-1); \
org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started:false);
units.0.instructions.configure=org.eclipse.equinox.p2.touchpoint.eclipse.setStartLevel(startLevel:2); \
org.eclipse.equinox.p2.touchpoint.eclipse.markStarted(started:true);
units.0.hostRequirements.1.namespace=osgi.bundle
units.0.hostRequirements.1.name=org.example.bundle
units.0.hostRequirements.1.range=[3.6.0.v20100503,3.6.0.v20100503]
units.0.hostRequirements.1.greedy=false
units.0.hostRequirements.2.namespace=org.eclipse.equinox.p2.eclipse.type
units.0.hostRequirements.2.name=bundle
units.0.hostRequirements.2.range=[1.0.0,2.0.0)
units.0.hostRequirements.2.greedy=false
units.0.requires.1.namespace=osgi.bundle
units.0.requires.1.name=org.example.bundle
units.0.requires.1.range=[3.6.0.v20100503,3.6.0.v20100503]
units.0.requires.1.greedy=false
This level of configuration could easily confuse someone without a p2 background and will likely be the cause of additional support requests.
Feature explosion
Forcing bundle specific configuration into features can cause the number of features required to describe a system to increase, possibly even into a situation where you may have to have a feature for each bundle. Imagine a set of virtual components that consist of bundles from a single pool: bundle 1, bundle 2, and bundle 3. Component A requires bundle 1 and bundle 2. Component B requires bundle 1 and bundle 3. The components described could easily be implemented using a single feature for each component.
We'll change our example slightly now to include the use of the start level functionality. We'll say that bundle 1 must start at level 10 and bundles 2 & 3 must start at level 20. We are faced with a difficult decision now since we must assign our start levels from a feature. Our choices are to duplicate the start level configuration for bundle 1 in both the Component A and Component B features, or created a new feature that contains only the single bundle 1 and its start level configuration. The features for Component A & B would be modified to require the third feature instead of the bundle itself and our system is described with three features instead of only two.
The number of bundles could grow pretty quickly if most bundles required a specific start level to be assigned. It also has the disadvantage that my framework will now impose arbitrary feature packaging requirements on those who chose to build components for it.
Effect on maintainability
As pointed out in the previous section, the possible need to duplicate configuration can be a source of regressions as changes are made to the platform. Another source of concern is the need to use hard-coded version numbers in the feature-oriented configuration.
Since the configuration will be stored separate from the bundle project and possibly in multiple locations, this presents a challenge for developers and especially for multi-developer teams whose developers are geographically distributed. There would be a significant effort involved to keep track of all locations the configuration is located and update it each time there is a change in version support. This would be considerably easier if the configuration was co-located with the bundle the configuration was applied to.
It is also bothersome that you are manually replacing an auto-generated configuration artifact. It adds a requirement to all developers who use this approach to be up-to-date on changes made to this configuration. At some point in the future, the p2 maintainers may see fit to add additional items or instructions to the auto-generated CU. These possibly critical instructions would not be present in the manually generated CU and could cause compatibility issues in the future.
Suggested solution
I like the approach taken in the patch that was submitted to the bug listed above, however I would expand on it slightly. You could implement a sort of scoping in the way you execute and apply the instructions available for a bundle. You can think of bundle configuration coming from one of three domains: p2 auto-generated (default), bundle developer provided (local), and packaging and deployment (container).
All instructions provided by the default scope would be executed first. This would provide reasonable values for most configuration items and perform basic operations like bundle install. The local scope, if provided in the form of a p2.inf file in the bundle's META-INF directory, would be executed next. Finally any CUs defined for the bundle within features would be executed in order based on the number of host requirements, with CUs with the least requirements being executed first.
This would allow the basic default behavior to apply in all cases where modification of these items is not required. If the bundle developer wishes to apply some well-informed configuration values, they can locally to the bundle. And lastly, if a particular packager or deployer wishes to make modifications to the bundle developer's wishes, they can as well.
|
|
|
Powered by
FUDForum. Page generated in 0.04491 seconds