Registry Resolver – Pseudo Code and Notes

Last updated April 15, 2003

if (registry is already resolved)
   stop now – nothing to do

foreach (plugin in the registry) do {
   if (the plugin is missing any of the required fields)
      generate an error and disable this plugin
   make an IndexEntry for this plugin id (if one doesn’t exist)
   put this plugin in the version list of this IndexEntry in order from largest down
} end foreach

foreach (fragment in the registry) do {
   if (the fragment is missing any of the required fields)
      generate an error and ignore this fragment
   make sure all prerequisites for this fragment exist (bug 10799)
   find the plugin for fragment.getPluginId() + fragment.getPluginVersion() using 
      the matching rules described in class PluginVersionIdentifier (bug 5668)
   if (the plugin is not found)
      generate an error and ignore this fragment
   add this fragment to the plugin’s list of fragments (plugin.fragments)
} end foreach

foreach (plugin in the registry) do {
   foreach (fragment in this plugin’s fragment list) do {
      if (this is the most recent version of this fragment in this list)
         add the components of this fragment to this plugin
      if (this fragment adds a library to the plugin)
         make sure we don’t have duplicate library entries in the plugin (bug 23522)
   } end foreach
   remove any fragments not contributing to the plugin (i.e. older versions of a fragment) (bug 10719 and 10720)
} end foreach

identify all the root plugin ids
foreach (root plugin id) do {
   identify the latest version of the root and use it only
   build the ConstraintsEntry -> Constraints structure under this IndexEntry  
   recursively build these structures for each prerequisite
} end foreach

NOTE: When the above foreach is done, each plugin id will have an associated IndexEntry -> ConstraintsEntry -> Constraints structure. For a given plugin id, the associated IndexEntry, etc. will identify all plugins which require this one as a prerequisite and all conditions which much be satisfied. The building of these structures must also remove any prerequisite loops and remove any conflicts. This may require disabling some plugins and removing portions of the ConstraintsEntry -> Constraints structure under a particular IndexEntry.

foreach (IndexEntry) do {
   foreach (ConstraintsEntry) do {
      find the plugin descriptor that is a best match for this group of Constraints
   } end foreach (ConstraintsEntry)
} end foreach (IndexEntry)


disable all plugin descriptors


foreach (IndexEntry) do {
   foreach (ConstraintsEntry) do {
      enable the plugin found to be the best match in the previous loop
      find the PluginPrerequisite that this plugin descriptor satisfies
      tell the PluginPrerequisite which version we resolved to
   } end foreach (ConstraintsEntry) 
} end foreach (IndexEntry)
	
optional – remove all disabled plugins from the registry
optional
foreach (enabled plugin in the registry) do {
   foreach (extension in this plugin) do {
      find the associated extension point
      if (extension point not found){
         generate an error
         NOTE: Should the plugin be disabled?
      } endif
      add this extension to the list of extensions for the found extension point
   } end foreach (extension) 
} end foreach (enabled plugin)
end optional

Some Notes:

A plugin with no extensions or extension points is termed a ‘library’ plugin.

Version Numbers are matched as follows:

  • if no version number is specified, pick the latest version that satisfies any other related constraints
  • if only a version number is specified, ensure a ‘compatible’ match
  • for exact matching rules, check out class PluginVersionIdentifier
  • multiple versions of a particular plugin id are allowed only if there are no extensions or extension points in any of the versions
  • we always try to use at most 1 version of a plugin even if all plugins are ‘library’ plugins.
  • Example (all plugins are library plugins):

  • pluginA version 1.2.0 requires pluginB and pluginC version 1.0.0
  • pluginB version 1.0.0 has no requirements
  • pluginB version 2.1.0 requires pluginC
  • pluginC version 1.0.0 has no requirements
  • pluginC version 1.1.0 has no requirements
  • pluginC version 2.0.5 has no requirements
  • Resolving will give us the following plugins:

  • pluginA version 1.2.0 requires pluginB and pluginC version 1.0.0
  • pluginB version 2.1.0 requires pluginC
  • pluginC version 1.1.0 has no requirements
  • Ideally, pluginB could have used pluginC version 2.0.5.

    Internal RegistryResolver structures:

    IndexEntry:

  • one index entry for each plugin id
  • each index entry has a pointer to each of the plugin descriptors that have this id
  • the pointers of plugin descriptors are in version order from the largest version to the smallest
  • each index entry also has a list of ConstraintsEntry’s (each ConstraintsEntry represents a group of constraints that can all be satisfied without conflict)
  • there is no ordering of ConstraintsEntry’s (just the order in which they are built)
  • ConstraintsEntry:

  • represents a group of constraints on a particular plugin id that can all be satisfied without conflict
  • these constraints are kept in a list
  • a new ConstraintsEntry is not created if multiple versions of this plugin are required and extensions or extension points exist
  • when an IndexEntry is created, the first (empty) ConstraintsEntry is also created (therefore, the first invocation must use this first ConstrainsEntry while other invocations may need to create a new one, if concurrent versions of this plugin are allowed)
  • the ConstraintsEntry points back to it’s parent IndexEntry
  • Constraint:

  • represents a particular prerequisite in a plugin
  • a Constraint hangs off a ConstraintsEntry and a ConstraintsEntry hangs off an IndexEntry. The ‘parent’ plugin descriptor in the Constraint is one of the same plugin descriptors that can be found in the version list for this IndexEntry
  • each Constraint exists in only one ConstraintsEntry but a ConstraintsEntry can have multiple Constraints
  • the Constraint points back to its parent ConstraintsEntry
  • Cookie:

  • used primarily as a tool to communicate any errors back to the calling methods (and thus terminate processing of a particular area of the tree)
  • keeps a list of all Constraints that have been successfully evaluated to the current point of execution. This list of constraints is also our was of ensure we don’t have any circular dependencies. If we have already have a constraint in this list with the same PluginPrerequisite as the one we are currently trying to resolve, we know we have a circular dependency.
  • Orphaned Subtrees:

    If a conflict arises such that a particular subtree becomes orphaned, the root of this subtree will be considered a root node and the remainder of the subtree (if it resolves correctly) will remain intact. Consider the following example (pluginResolveTest_15):

    PluginA 1.2.0 requires PluginB 2.1.0 and PluginC 1.0.0
    PluginB 1.0.0 has no requirements but does have an extension and extension point
    PluginB 2.1.0 requires PluginC 2.0.5 (exact match)
    PluginC 1.0.0 has no requirements
    PluginC 1.1.0 has no requirements but have an extension and extension point
    PluginC 2.0.5 has no requirements

    In this case, a conflict (non-resolvable because of extensions and extension points) exists in PluginA and it is disabled. This causes PluginB to be orphaned and it is then considered a root. Resolution of the above example will return a registry containing PluginB 2.1.0 and PluginC 2.0.5. While this may seem counter-intuitive, consider the case where PluginA was a PDE plugin and PluginB was the JDT plugin. JDT can live without PDE and the subtree really should survive the conflict in PDE’s plugin.

    Soft Prerequisites (Optional clause):

    We now have the notion of prerequisites which are optional, called soft prerequisites. These prerequisites must have the clause ‘optional=”true”’. Consider the following xml statement:

    <requires>
       <import plugin="tests.c" version="2.1.0" optional=”true”/>
    </requires>

    This prerequisite will be ignored if any of the following conditions apply:

  • the plugin ‘tests.c’ does not exist at all
  • the plugin ‘tests.c’ does exist but there are no versions compatible with version number 2.1.0
  • the plugin ‘tests.c’ exists and there is a version compatible with 2.1.0 but the addition of this plugin will cause a conflict. A conflict arises if a particular root plugin requires more than one version of ‘tests.c’, the versions are not compatible with each other, and at least one of the versions contains a minimum of one extension or extension point
  • If an optional prerequisite is ignored for any of the reasons listed above no error message is generated (but an informational message may be output to indicate that this prerequisite is being ignored). Ignoring an optional prerequisite does not cause any plugins to be disabled. This causes potential error conditions to be more complex. Each time we have a situation where we have a conflict, we must first check to see if this is an optional prerequisite. The same is true of situations where we expect to find an associated IndexEntry (or other structure).

    If a prerequisite is ignored, the Constraint structure associated with this particular parent/prerequisite pair is not added into the IndexEntry, ConstraintsEntry, Constraint structure. None of the existing structures or pointers to them are altered or removed. We cannot tell, without re-examining every relationship associated with this particular root plugin, which portions were used for other relationships (and not just the relationship leading to this ignored prerequisite).

    Question on ‘lastResolved’ field:

    The ConstraintsEntry structure contains a field called ‘lastResolved’ which is set but does not seem to be used. At the same time, there are numerous places where we examine all of the Constraints off a ConstraintsEntry structure to determine which plugin (if any) is the best one to resolve these Constraints compatibly. Can we use ‘lastResolved’ to try and minimize these? Each time we look for a best match plugin, we cycle through all versions of this plugin and all Constraints off this ConstraintsEntry.