Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Virgo/Concepts
OSGi
Virgo is based on OSGi a java modular system created in 1999 by the OSGi Alliance. Virgo uses Eclipse Equinox which implements the OSGi core framework specification. Virgo also depends on some non-standard features of Equinox and so does not run on other OSGi framework implementations.
OSGi is a component model with dynamic life-cycle. You can compare OSGi modules with UPnP. That mean you can extend/remove features while the VM is still running. Unlike deploying a war, for example, that needs a restart of the VM to add new features, OSGi allows you to just hot deploy new modules or update existing modules.
If you wish to learn more about OSGi, the following books are recommended:
- Modular Java Creating Flexible Applications with OSGi and Spring (this book is great to learn the basics about OSGi, especially since you start from scratch, then move on to something with spring inside)
- Pro Spring Dynamic Modules for OSGi Service Platforms (based on Spring DM, which is the old blueprint that is available in virgo)
- Java Application Architecture Modularity Patterns with Examples Using OSGi (Probably the best one for good understanding on "how to do stuff" properly and with efficiency)
Artifacts
(aka artefacts)
Virgo supports the deployment of various types of artifact. Standard artifact types are:
- bundle - an OSGi bundle;
- configuration - a properties file with name of the form <pid>.properties which when deployed produces a Configuration Admin dictionary with PID <pid>;
- plan - an XML file which refers to arbitrary other artifacts in the Virgo repository -- plans may be scoped or unscoped, as described below, and atomic or non-atomic in terms of whether the lifecycle of the plan is tied to the lifecycle of the plan's artifacts; and
- PAR - a Plan ARchive which contains a collection of artifacts and is equivalent to a scoped, atomic plan referencing those artifacts (except the artifacts need not be present in the Virgo repository).
In addition, it is possible to add user-defined artifact types.
Kernel
The Virgo kernel is the core runtime which may be used on its own or to deploy one or more server types and applications for those server types. The kernel houses the deployment pipeline, described below, as well as support for common artifact types (bundle, configuration, plan, and PAR), regions, scoping, and other core Virgo features.
Currently, for example, the Virgo web server packaging build uses the kernel to deploy a web server plan which includes the Gemini web container and the Virgo web bundle (which integrates Gemini web into Virgo).
The startup scripts launch the Equinox launcher which then installs, and optionally starts, each of a configurable collection of bundles to provide kernel function. See the User Guide for details of how to configure the kernel.
Regions
The kernel uses the Equinox region support to isolate the kernel from application artifacts, including artifacts which implement servers. A region is a subset of the bundles in the OSGi framework which have controlled sharing with other regions. As shown in the figure below, the kernel starts in a normal OSGi framework, known as the kernel region, and then creates a region known as the user region.
Region support was added to enable applications to run with a different version of Spring than that used by the kernel. A minimal set of Spring bundles is installed into the kernel region with very few optional dependencies which keeps the kernel footprint and startup time low. In principle, Spring could be entirely removed from the kernel region if the kernel was modified not to depend on Spring (most of these dependencies are because the kernel uses Spring DM to publish and find kernel services).
Certain packages and services are imported from the kernel region into the user region and certain services, but no packages, are exported from the user region to the kernel region. This isolates the kernel region from interference due to types and package wirings in the user region. The configuration file config/org.eclipse.virgo.kernel.userregion.properties controls the importing of packages and services into and the exporting of services out of the user region.
So, apart from the basic principle that no packages are exported from the user region to the kernel region, there is a lot of flexibility for changing the contents of both kernel and user regions and for specifying which packages and services are shared between the regions.
In future, Virgo could be extended to support multiple user regions in order to isolate applications from each other.
Scoping
Virgo adds the concept of scoping to OSGi. The main use case for scoping is where a group of bundles form an application which needs to avoid clashing with other applications and which needs reliable behaviour when it calls third party bundles which use thread context class loading. Clashes can occur because of bundles, packages, or services conflicting in some way.
Metadata Rewriting
Virgo rewrites the metadata of bundles in a scope to prefix the bundle symbolic names with a scope-specific prefix and to add a mandatory matching attribute, with a scope-specific value, to packages exported by bundles in the scope.
Virgo also uses the standard OSGi service registry hooks to limit the visibility of services published by bundles in a scope.
However, a bundle in a scope may access bundles, packages, and services not provided in the scope but which are available outside the scope, that is from unscoped bundles. So a scope acts similarly to a programming language scope such as Java's curly braces:
int x; // b is not visible here { int b; // both b and x are visible here }
Synthetic Context Generation
To ensure reliable thread context class loading when third party bundles are called from a scope, Virgo generates a synthetic context bundle in the scope. The class loader of the synthetic context bundle is used as the thread context class loader when bundles in the scope make calls outside the scope. The synthetic bundle imports each of the other bundles in the scope using the Virgo import-bundle header. This is semantically equivalent to importing all the exported packages of the other bundles in the scope. So to make a package of a scoped application available for thread context class loading, it is simply necessary to export the package.
Example of Scoping
The figure below shows a scoped plan referring to two bundles A and B being deployed. The result is a scope containing the bundles A and B as well as the synthetic context bundle. Note that bundles inside the scope can access bundles, such as X, outside the scope. Also, bundles outside the scope, such as Y, cannot access bundles inside the scope.
Pipeline
Artifacts are deployed into Virgo using a deployment pipeline consisting of several pipeline stages some of which have pipelines nested inside them as shown in the figure below.
The pipeline, and each pipeline stage, accepts a tree of install artifacts as input and outputs a possibly modified tree. The deployment pipeline is constructed by the Plumber class.
Transformers
Many of the interesting modifications to the tree are performed by the transform stage which uses the whiteboard pattern to drive all services of a Transformer type in order of service ranking. A number of standard Transformer services are defined in the Spring context file deployer-context.xml in the kernel's deployer bundle. Some interesting examples of standard Transformers are:
- PlanResolver which takes as input a tree consisting of a single plan node and adds a subtree representing the content of the plan, including any nested plans and their subtrees,
- ScopingTransformer which rewrites the metadata of a subtree rooted in a scoped plan and gathers service scoping information for the subtree,
- SyntheticContextBundleCreatingTransformer which adds a synthetic context bundle as a child node of a scoped plan, and
- ImportExpandingTransformer which converts Virgo-specific headers such as import-bundle into standard OSGi import-package statements.
Quasi Framework
The quasi framework is an abstraction of the Equinox State and is used in auto-provisioning missing dependencies during deployment. The quasiInstall stage installs the bundles in the input tree into an instance of the quasi framework. The quasiResolve stage attempts to resolve these bundles and auto-provision any missing dependencies from the Virgo repository by installing them in the quasi framework instance. The commit stage attempts to install the bundles in the input tree, along with any auto-provisioned bundles, into the OSGi framework.
See Quasi Framework Design for more details.
Exception Handling
There are two approaches to handling exceptions thrown by a pipeline stage. In general, unexpected exceptions are allowed to percolate upward and result in diagnostics and a failed deployment. However, certain expected exceptions, such as failure to resolve the dependencies of the install artifact tree, need to be handled more gracefully. In these cases, a compensating pipeline stage is defined which drives a compensation stage if an exception is thrown. failInstall and failResolve in the figure above are examples of compensation stages.
Repositories
Virgo repositories contain artifact URLs and metadata and are indexed by the Cartesian product of artifact type, name, and version. There are three kinds of repository: external, watched, and remote. Repositories are passive in the sense that changes to repository content do not cause artifacts to be deployed into Virgo, refreshed, or undeployed. Repositories support queries which allow sets of artifacts satisfying certain criteria, for example with a version in a given version range, to be determined.
External Repositories
External repositories are created by scanning a directory which contains artifacts, possibly in nested directories. The repository configuration specifies a pattern which says which files should be treated as artifacts. After the repository is created, changes to the directory do not affect the repository content.
The Virgo kernel's default repository configuration, in config/org.eclipse.virgo.repository.properties, specifies an external repository created from the repository/ext directory.
Watched Repositories
Watched repositories are created by scanning a directory which contains artifacts but no nested directories. All files in the directory are treated as artifacts. The directory is re-scanned periodically and the interval between re-scans is specified in the repository configuration. Changes detected by re-scanning are reflected in the repository content. Note that changing the content of a watched repository does not cause artifacts to be deployed into Virgo, refreshed, or undeployed.
The Virgo kernel's default repository configuration specifies a watched repository based on the contents of the repository/usr directory.
Remote Repositories
A remote repository refers to a repository hosted by a Virgo instance sometimes known as a repository server. The hosted repository is configured using the file config/org.eclipse.virgo.apps.repository.properties and may be either an external or a watched repository.
The remote repository is accessed by a Virgo instance sometimes known as a repository client. The repository client is normally a different instance of Virgo to the instance hosting the repository, but it can be the same instance which is handy for testing. The remote repository periodically downloads its content from the hosted repository. The period between downloads may be configured in the repository configuration. The remote repository also caches artifacts which have secure hashes associated with them in the hosted repository. Only bundles currently have secure hashes associated with them. The secure hash is used to determine when a cached artifact is stale and needs to be freshly downloaded.
Repository Chains
The Virgo repository is configured as a chain of external, watched, and remote repositories. The chain is a list which is searched in the configured order. The effect of this search order is that an artifact with a given type, name, and version which appears in more than one repository in the chain is only accessed from the first repository in the chain in which it appears. Abstractly, the repository chain behaves as a single repository, but its content may mutate in quite a different way to the content of an individual external, watched, or remote repository.