| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
| <html> |
| <head> |
| <title>Untitled Document</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
| </head> |
| |
| <body> |
| <h1>Registry Resolver – Pseudo Code and Notes</h1> |
| <p><em>Last updated April 15, 2003</em></p> |
| <pre>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</pre> |
| <p>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.</p> |
| <pre>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 |
| </pre> |
| <h3>Some Notes:</h3> |
| <p>A plugin with no extensions or extension points is termed a ‘library’ |
| plugin.</p> |
| <p>Version Numbers are matched as follows: |
| <li>if no version number is specified, pick the latest version that satisfies any other related constraints</li> |
| <li>if only a version number is specified, ensure a ‘compatible’ match</li> |
| <li>for exact matching rules, check out class PluginVersionIdentifier</li> |
| <li>multiple versions of a particular plugin id are allowed only if there are |
| no extensions or extension points in any of the versions</li> |
| <li>we always try to use at most 1 version of a plugin even if all plugins are |
| ‘library’ plugins.</li> |
| <p>Example (all plugins are library plugins):</p> |
| <li>pluginA version 1.2.0 requires pluginB and pluginC version 1.0.0</li> |
| <li>pluginB version 1.0.0 has no requirements</li> |
| <li>pluginB version 2.1.0 requires pluginC</li> |
| <li>pluginC version 1.0.0 has no requirements</li> |
| <li>pluginC version 1.1.0 has no requirements</li> |
| <li>pluginC version 2.0.5 has no requirements</li> |
| <p>Resolving will give us the following plugins:</p> |
| <li>pluginA version 1.2.0 requires pluginB and pluginC version 1.0.0</li> |
| <li>pluginB version 2.1.0 requires pluginC</li> |
| <li>pluginC version 1.1.0 has no requirements</li> |
| <p>Ideally, pluginB could have used pluginC version 2.0.5.</p> |
| <h3>Internal RegistryResolver structures:</h3> |
| <h4>IndexEntry:</h4> |
| <li>one index entry for each plugin id</li> |
| <li>each index entry has a pointer to each of the plugin descriptors that have this id</li> |
| <li>the pointers of plugin descriptors are in version order from the largest version to the smallest</li> |
| <li>each index entry also has a list of ConstraintsEntry’s (each ConstraintsEntry |
| represents a group of constraints that can all be satisfied without conflict)</li> |
| <li>there is no ordering of ConstraintsEntry’s (just the order in which they are built)</li> |
| <h4>ConstraintsEntry:</h4> |
| <li>represents a group of constraints on a particular plugin id that can all be |
| satisfied without conflict</li> |
| <li>these constraints are kept in a list</li> |
| <li>a new ConstraintsEntry is not created if multiple versions of this plugin |
| are required and extensions or extension points exist</li> |
| <li>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)</li> |
| <li>the ConstraintsEntry points back to it’s parent IndexEntry</li> |
| <h4>Constraint:</h4> |
| <li>represents a particular prerequisite in a plugin</li> |
| <li>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</li> |
| <li>each Constraint exists in only one ConstraintsEntry but a ConstraintsEntry |
| can have multiple Constraints</li> |
| <li>the Constraint points back to its parent ConstraintsEntry</li> |
| <h4>Cookie:</h4> |
| <li>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)</li> |
| <li>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.</li> |
| <h4>Orphaned Subtrees:</h4> |
| <p>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):</p> |
| <p>PluginA 1.2.0 requires PluginB 2.1.0 and PluginC 1.0.0<br> |
| PluginB 1.0.0 has no requirements but does have an extension and extension point<br> |
| PluginB 2.1.0 requires PluginC 2.0.5 (exact match)<br> |
| PluginC 1.0.0 has no requirements<br> |
| PluginC 1.1.0 has no requirements but have an extension and extension point<br> |
| PluginC 2.0.5 has no requirements</p> |
| <p>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.</p> |
| <h4>Soft Prerequisites (Optional clause):</h4> |
| <p>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:</p> |
| <pre><requires> |
| <import plugin="tests.c" version="2.1.0" optional=”true”/> |
| </requires></pre> |
| <p>This prerequisite will be ignored if any of the following conditions apply:</p> |
| <li>the plugin ‘tests.c’ does not exist at all</li> |
| <li>the plugin ‘tests.c’ does exist but there are no versions compatible |
| with version number 2.1.0</li> |
| <li>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</li> |
| <p>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).</p> |
| <p>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).</p> |
| <h4>Question on ‘lastResolved’ field:</h4> |
| <p>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.</p> |
| <p></p> |
| </body> |
| </html> |