About Dynamic Bundle Projects
This is a description of the concept behind an implementation of a bundle manager, named the InPlace Activator, handling the life cycle and relationship between OSGi bundles and Eclipse bundle source projects.
The use case is that when you save your bundle project, the save triggers a build, which automatically triggers an update loading the new compiled source into a bundle, and finally executes (start) the bundle on the platform where the source project resides.
This feature – Compile and Run on Save – brings a new dynamic programming environment normally associated with interpreted languages, where each repetitive save/compile/run cycle should be a smooth and fast one step user experience.
The Bundle Project and the Workspace Region
A bundle project has the following characteristics:
- Is a member of a workspace region, where the set of region bundle projects is a subset of the projects in a workspace.
- For a project to be a member in a region, and thus called a bundle project, the following must be satisfied:
- Has the java and plug-in nature enabled, implying the presence of a manifest file.
- The project is open and accessible.
- There is an unconditional one-to-one bidirectional relationship between the project and its bundle.
- A shared key (symbolic name and version) and a common location identifier must represent the implemented relationship between the project and the bundle.
All other workspace projects (e.g. pure java projects, closed projects, C++ projects, …) and external bundles (e.g. jar bundles) are excluded from the region.
The relationship between Workspace, Region, Bundle Projects and External Bundles is illustrated in the following simplified figure:
Bundle source projects can enter and leave a region. This happens when a project is Created or Deleted. A project is Created when imported, opened or created manually (usually by using the Plug-in Project wizard or a template) and Deleted when closed or deleted from the workspace. Update and Rename does not alter the membership status in a region. This covers all CRUD operations on a project related to its region membership.
Activating a Region and its Bundle Projects
A region is activated when at least one of its projects are activated. An activated project is assigned a bundle-project nature, binding a behavioral and structural relationship between the project and its bundle. Right after the assignment of the first project, its associated bundle is resolved and all deactivated project members are installed. When the last project in the region is deactivated, its nature is removed and all the bundle projects in the region are uninstalled.
In a deactivated workspace region all actions on projects are ignored, except for updating any displayed (e.g. in a property page) status information (e.g. renaming a project) about bundle projects.
Execute and Isolate Project Operations and Bundle Commands
CRUD operations can affect the membership of a project in a region and the state (activated or deactivated) of a region. In the same manner, a bundle command can change the state of a bundle project and thus the state of a region.
Uninstalling a bundle project from an external source (e.g. the Host OSGi Console) in an activated region violates the consistency of an activated region where all deactivated bundles must be in state installed and activated bundles must be resolved. In such cases, actions are taken to maintain the consistency.
The following rules apply for project operations and bundle commands in an activated region:
- It is allowed to Refresh, Update, Start and Stop activated bundle projects. These commands does not affect the state of an activated region
- It is not allowed to Resolve a deactivated bundle project. This implies that all commands that require a resolved bundle will be unavailable. It requires a resolved bundle to Start and Stop a deactivated bundle project. Refresh and Update are allowed on installed (deactivated bundle projects) bundles.
- It is not permitted to uninstall a bundle in an activated region. When uninstalling a bundle project, activated or deactivated, the region must either be deactivated or the uninstalled bundle must be installed (deactivated bundle project) or resolved (activated bundle project) to maintain the definition of an activated region.
- If a project is created by importing or opening it, and the project is in an activated (has the bundle-project nature enabled) mode in a deactivated region, the region is activated and the activated bundle project is resolved. If the bundle project is in a deactivated mode the bundle project is installed.
- If an activated bundle project is deleted by closing or deleting it and the bundle project is the last activated project in the region, the region is deactivated.
When deactivated, the region is passive and does not respond to any bundle commands. This defines a behavioral border between region bundles and external bundles where bundle commands are isolated to an activated region and any external bundle command in a deactivated region is allowed.
Composite Bundle Commands
Based on the concept of a region, a set of composite bundle commands should follow naturally.
Activate is a composite command, enabling the bundle-project nature before installing, resolving and starting a bundle project. If this is the first activated project, all remaining deactivated bundle projects are installed. In the same manner Deactivate, disables the bundle-project nature and unresolves the activated project moving the bundle project to state installed. If this is the last activated project, all bundle projects are uninstalled and refreshed.
Dynamic Bundle Projects
In this context, dynamic means that a bundle should respond to changes in its corresponding source project. A detour is to uninstall and then install, resolve and start an activated bundle after its project has been built. The composite Reset command does exactly this, although its purpose is to manually request for a clean region state.
When first activated, any changes in the project is captured and is reflected in the running instance of the bundle. The solution is to use the OSGi Update command, which in many ways is a composite command in itself, unresolving the bundle before it is updated. Activated bundle projects are by default updated automatically behind the scenes in concert with the Automatic Build option when a project is saved. This is accomplished by utilizing the bundle-project nature and the OSGi resolver hook. To control the update frequency of a bundle project there is an option to update the bundle manually from the UI after it has been built.
Bundle Closures and Dependencies
When the IDE starts up and shuts down bundles are resolved/started and stopped/unresolved in dependency order. The order is configurable by setting start levels, assigning lazy activation policies on bundles and the like. For bundle projects, this must be handled continuously when source projects are changed and when entering and leaving a region. Bundle projects enter a region at IDE startup and when Created and leaves a region at shutdown and when Deleted. Changed projects stay in the region by being updated and returning to the same state as before update, but may introduce new dependencies (e.g. adding a package to the Import-Package header).
The set of bundle projects in a region contains one or more partitions or closures, where each partition is a directed acyclic graph and the dependency relation between bundle projects in the graph is defined by different (e.g. Import-Package and Require-Bundle directives) headers in the manifest file. Depending on the direction and scope of a dependency a partial dependency order of bundle projects is constructed to be included in a bundle operation to execute. Thus it is possible to traverse and sort the set of region bundle projects in dependency (e.g. requiring or providing) order, based on a random set of initial bundle projects.
As mentioned, a closure defines a subset of dependent bundle projects in a region. For instance, a Requiring Closure is constructed from an initial set of bundles that directly and indirectly (transitive) requires capabilities from other bundle projects in a region. Inversely, the Providing Closure is constructed from an initial set of bundles containing bundles that directly and indirectly (transitive) provides capabilities to this initial set of bundle projects in a region.
Dependencies between uninstalled bundle projects are obtained by examining the manifest file of each bundle project or utilizing the Eclipse resource API to obtain this information. For installed and resolved bundles – that is in an activated region -, the OSGi wiring API is used.
In principle, the only type of closure that exist between region bundles and other external (non member) bundles (e.g. jar bundles) is the Requiring Closure. Therefore, external bundles, does not require, but only provide capabilities to region bundle projects. This defines a structural dependency border between region bundle projects and external bundles.
The InPlace Bundle Activator
The definition of a behavioral and a structural boundary for a region as a set and bundle projects as members provides a simple model for implementing a bundle manager. The heavy lifting is done by the Eclipse API’s grounded on the Equinox OSGi implementation which provides the mechanisms to manage OSGi bundle projects as components in a dynamic way.
Thanks for reading