53 Resolver Hook Service Specification

53.1 Introduction

The module layer is responsible for the resolve operation that wires requirements (Import-Package, Require-Bundle etc.) to capabilities (Export-Package, Bundle-SymbolicName/Bundle-Version etc.). The resolve operation is designed to work stand-alone but in certain use cases it is important to be able to influence the resolver's decisions. This specification defines a Resolver Hook Factory service that allows third party bundles to influence the resolve operation. Primary use cases for this hook are scoping of groups of bundles. However, the hooks also allows bundle to find out about, and control, the resolve operation for other purposes.

53.1.1 Essentials

  • Reduction - Allow third party bundles to remove capabilities from a set of capabilities that matches a requirement.

  • Complete - Support all built-in namespaces as well as the generic capability and requirement headers.

  • Singletons - Allow third party bundles to remove candidates from a set of singleton capabilities before the resolver selects one.

  • Secure - Support full security but allow operation without security present.

53.1.2 Entities

  • Resolver Hook Factory - The service registered by a bundle that wants to control the resolve operation. This service is used to create an instance of the Resolver Hook for each resolve operation.

  • Resolver Hook - Is created by the Resolver Hook Factory service for each resolver process. Is consulted to reduce candidate capabilities for resolving and selecting singletons.

  • Client - A bundle that is considered during the resolve operation.

  • Handler - A bundle that registers a Resolver Hook Factory service for influencing the resolve operation.

  • Bundle Capability - A capability represents a feature of a bundle described with attributes and directives defined in a namespace. Some namespaces are built-in to the OSGi framework, others are generic.

  • Bundle Requirement - A requirement represents a need from a bundle described as a filter on the attributes of a Bundle Capability.

  • Provider - A bundle that provides a Bundle Capability.

  • Consumer - A bundle that requires a Bundle Capability

  • Resolver - The internal framework machinery that resolves requirements to providers with matching capabilities.

Figure 53.1 Resolver Hooks Entities

Resolver Hooks Entities

53.1.3 Synopsis

A handler bundle that needs to manage the wiring between bundles must register a Resolver Hook Factory service. For each resolve operation the Resolver needs to perform, the framework asks each Resolver Hook Factory service for a new Resolver Hook specific for the operation. During the resolve operation, the Resolver will allow the Resolver Hooks to remove candidate solutions and assist in selecting singletons.

53.2 Resolve Operation

53.2.1 Trigger Bundles

The Resolver is triggered by activity in the OSGi framework. Calling certain methods on a bundle when a bundle is in the INSTALLED state will cause the framework to begin a resolve operation in order to resolve the bundle. Other API can also trigger a resolver. Frameworks can resolve on a per bundle basis or they can resolve a number of bundles atomically in one operation. The bundles that trigger a resolve operation are called the trigger bundles. The trigger bundles can be defined by the following cases:

  • Root Bundle - Calling certain methods on a bundle when a bundle is in the INSTALLED state will cause the framework to begin a resolve operation in order to resolve the bundle. In general, a bundle needs to be resolved when its class loader is needed. The following Bundle methods will start a resolve operation when the subject is not yet resolved:

    • start

    • loadClass

    • findEntries

    • getResource

    • getResources

  • Resolve Bundles - The set of bundle revisions of the unresolved bundles given as argument, or their default when null is used, to the Framework Wiring resolveBundles method. See Using the Wiring API.

  • Refresh Bundles - A refresh operation will have to re-resolve the bundles that will be unresolved in the refresh. The trigger bundles are then the bundle revisions of the dependency closure, which is described in Refreshing.

  • Dynamic Import - A Dynamic Import can require the framework to start a resolve operation.

  • Other - The Resolver Hook is a service so other parties can to start a resolver operation to run what-if scenarios or for other purposes.

53.2.2 Resolving Types

Various types of resolve operations can be initiated:

  • Static - A static bundle resolve operation. This resolve operation is necessary any time one or more bundles transitions from the INSTALLED state to the RESOLVED state. During this resolve operation the framework attempts to resolve static requirements specified by the bundles being resolved.

  • Dynamic - A dynamic import must be resolved.

The resolve operation is not limited to the trigger bundles only, they just provide the root bundles that must be resolved. The Resolver is free to consider any present bundle to provide the required capabilities. If such bundles are in the INSTALLED state then they can also become a candidate to be resolved. The resolver is also free to use bundles that are actually not installed in the framework, for example for what-if scenarios.

53.2.3 Preparing Handlers

Once the resolver is triggered, it must prepare the Handlers to participate in the resolve operation. A Handler is an active bundle that needs to participate in the resolve operation, there can be multiple Handlers in a framework. A Handler must register a Resolver Hook Factory service. This service is the interface between a Handler bundle, a bundle that will handle some aspects of the resolve operation, and the Resolver.

To prepare the Handlers, the Resolver must request a new Resolver Hook from each of the registered Resolver Hook Factory services with the begin(Collection) method. The parameter is the set of trigger bundles. The Handler is expected to create a new ResolverHook object for each call. If null is returned then the Handler abstains from participation. A Resolver Hook Factory must be thread-safe and allow the creation of independent Resolver Hook objects that can be active on multiple threads.

A Resolver Hook is created for a single atomic resolve operation and does not have to be thread safe. The Resolver must ensure that access to the Resolver Hook is serialized, that is, the can only be active from a single thread. The ResolverHook object is called multiple times during a resolve operation to influence the outcome of a resolve operation. The following operations are provided:

A Resolver Hook can influence the outcome of a resolve operation by removing entries from shrinkable collections. A shrinkable collection is a collection that can only shrink. The Handler can remove entries from the collection but it cannot add an entry to it. Any attempt to add to the collection must result in throwing an Unsupported Operation Exception. All collections in the Resolver Hook API are shrinkable collections.

The Resolver Hook Factory services begin(Collection) method is called in the ranking order, see Service Ranking Order. This is the same order used for calling the resulting Resolver Hooks. The Resolver Hook end() method notifies the Handler that the resolve operation has finished. The framework must not hold on to this Resolver Hook instance after the end() method has been called.

53.2.4 Limiting the Set of Resolvable Bundles

There are use cases where a bundle that is being installed should not be allowed to resolve until some activity has taken place. Sometimes certain bundles should never resolve. For example, there are byte code weaving scenarios where a bundle is used as the source but a synthetic bundle with the woven code provides the actual classes. The source bundle should then not resolve. The filterResolvable(Collection) method can be used to ensure that certain bundles are never resolved. All the given Bundle Revisions are unresolved. The Handler can look at the given collection and remove any bundles it wants to prevent being resolved in this resolve operation.

The set of bundles passed will contain the trigger bundles. If a Handler removes one of the trigger bundles the resolve of the removed bundle will not succeed and fail the call to the method that triggered the resolve operation.

The framework can pass an empty collection of resolvable bundle revisions, this could for example happen while resolving a dynamic import. An empty collection indicates that the framework will not cause any bundles to transition from INSTALLED to RESOLVED during a dynamic import package resolving.

For example, a Handler wants to ensure certain bundles are not resolved, then it can do:

public class UnresolveHandler implements ResolverHook{
    Set<BundleRevision> neverResolve = ... ;

    public void filterResolvable(
        Collection<BundleRevision> toBeResolved ) {
        toBeResolved.removeAll( neverResolve );
    }

   ... other methods
}

53.2.5 Hiding Capabilities

The filterMatches(BundleRequirement,Collection) method is used to remove capabilities for consideration for a specific requirer. The Handler receives the Bundle Requirement and the set of candidates that already match the requirement. The Handler can now remove any candidates that are not suitable. Removing the capability will prevent the requirement from getting wired to the capability. If the Bundle Requirement is declared in a fragment then the host is not knowable.

For example, a Handler wants to ensure that a set of bundles in a group are only wired to a limited set of infra-structure bundles and each other. This could be implemented as follows:

public class GroupHandler implements ResolverHook{
    Map<Bundle,Set<Bundle>> groups = ...;
    Set<Bundle> system = ... ;

    public void filterMatches( BundleRequirement r,
        Collection<BundleCapability> candidates ) {
        Set<Bundle> group = groups.get(r.getRevision().getBundle());
        if ( group == null )
            return; // not in a group

        for ( Iterator<BundleCapability> i = candidates.iterator();
            i.hasNext(); ) {
            BundleCapability candidate = i.next();
            Bundle other = candidate.getRevision().getBundle();

            if ( group.contains(other) || 
                system.contains(other) )
                continue;

            i.remove(); // not system, not in the same group
        }
    }

   ... other methods
}

53.2.6 Effect of Singleton Capabilities

Certain namespaces provide a singleton directive. For example, the osgi.wiring.bundle namespace defines that a bundle can be singleton, meaning that only one such bundle with a given symbolic name can be resolved. The purpose is to ensure that a bundle that needs exclusiveness gets this.

In certain scenarios it is necessary to limit the singleton constraint to a group of bundles instead of the whole framework. One of the primary use cases of the Resolver Hooks is to allow scoping of bundles. Some Handlers can interpret the singleton constraints as to apply to the group, not the whole framework. For this purpose, the Resolver Hook API allows the Handler to influence which bundle revision is selected for the singleton with the filterSingletonCollisions(BundleCapability,Collection) method.

The first parameter is the capability that is under consideration by the resolver, called the viewpoint capability. The resolver needs to find out what other capabilities can collide with the viewpoint. A collision takes place when multiple bundles with the same symbolic name and singleton directive set to true can potentially be resolved at the same time. For example, a Handler implements a grouping model. A singleton is therefore only valid for the bundles in this group. A Handler must therefore be able to indicate which bundles can collide. This model is asymmetric. If a group has for example outer and inner bundles, then inner bundles can collide with outer bundles but not vice versa.

The second parameters of the filterSingletonCollisions(BundleCapability,Collection) method is a set of capabilities called the candidates. The Handler can shrink this collection by removing capabilities. Removing a capability from the list of collision candidates will effectively hide the collision candidate from the target singleton bundle. This will allow the target singleton bundle to resolve regardless of the resolving state of the collision candidate.

If S is the group of infrastructure bundles (acting as an outer bundles) and a non-infrastructure group is A, then the following cases exist:

S S     collide, leave in set
A A     collide, leave in set
A !A    remove from set, not visible
A S     collide, leave in set
S A     do not collide, remove from set

The following example implements this strategy:

public class GroupHandler implements ResolverHook{
    Map<Bundle,Set<Bundle>> groups = ...;

    public void filterSingletonCollisions( BundleCapability c,
        Collection<BundleCapability> candidates ) {
        Set<Bundle> group = groups.get(c.getRevision().getBundle());

        for ( Iterator<BundleCapability> i = candidates.iterator();
            i.hasNext(); ) {
            BundleCapability candidate = i.next();
            Bundle other = candidate.getRevision().getBundle();
            Set<Bundle> otherGroup = groups.get(other);

            if ( group == otherGroup || otherGroup == null ) // Samegroup
                continue;

            i.remove(); // not system, not in the same group
        }
    }

   ... other methods
}

The framework can call this method multiple times for the same singleton capability. For example, as a first pass a framework may want to determine if a singleton bundle is resolvable first based on the presence of other already resolved singleton capabilities. Later the framework may call this method again to determine which singleton capabilities from unresolved bundles to allow to resolve.

53.3 The Resolve Operation

The following steps outline the way a framework uses the resolver hooks during a resolve operation. Any callbacks to the hook services must be done in a privileged block, see Privileged Callbacks.

  1. Collect a snapshot of registered Resolver Hook Factory services that will be called during the current resolve operation. If a Resolver Hook Factory contained in the snapshot unregisters then the resolve must fail, see Failures. Each registered Resolver Hook Factory service in the snapshot will be obtained by the framework through the system's bundle context.

  2. For each Resolver Hook Factory in the snapshot, in ranking order, call the begin(Collection) method to inform the Handler about the begin of a new resolve operation. This method must return a Resolver Hook. If this hook is not null, must be added to list H. If a corresponding Resolver Hook Factory service is unregistered then the Resolver Hook must be removed from H.

  3. Determine the collection of unresolved bundle revisions that may be considered for resolving during the current resolve operation and place each of the bundle revisions in a shrinkable collection U(nresolved).

    • For each active Resolver Hook in H(ooks), call the filterResolvable(Collection) method with U. The Handler can remove any candidate that should not be resolved.

    U now contains all the unresolved bundle revisions that potentially could be resolved during this resolve operation. Any bundle revisions that were removed by Handlers must not resolve in this operation.

    This step can be repeated multiple times interspersed with other calls except to the begin and end methods.

  4. S = {}

  5. For each bundle revision B in U that represents a singleton capability:

    • Determine the collection of available capabilities that have a namespace of osgi.wiring.bundle, are singletons, and have the same symbolic name as the singleton bundle revision B and place each of the matching capabilities into a shrinkable collection S.

    • Remove the osgi.wiring.bundle capability provided by the bundle revision B from S. A singleton bundle cannot collide with itself.

    • For each resolver hook call the filterSingletonCollisions(BundleCapability,Collection) method with the osgi.wiring.bundle capability provided by bundle revision B and S.

    S now contains all the singleton osgi.wiring.bundle capabilities that can influence the ability of bundle revision B to resolve.

    This step can be repeated multiple times interspersed with other calls except to the begin and end methods.

  6. During a resolve operation the Resolver can resolve any or all bundles contained in U. For each bundle revision B in U which the Resolver attempts to resolve the following steps must be followed:

    • For each requirement R(equirement) specified by bundle revision B, determine the collection of capabilities that satisfy the requirement R and place each matching capability into a shrinkable collection C(apabilities). A capability is considered to satisfy a particular requirement if its attributes match the requirement's filter and the requirer bundle has permission to access the capability.

    • For each Resolver Hook in H, call the filterMatches(BundleRequirement,Collection) method with the Bundle Requirement R and the shrinkable collection C.

    C now contains all the capabilities that can be used to satisfy the requirement R. Any other capabilities that got removed from C must not be used to satisfy requirement R.

    This step can be repeated multiple times interspersed with other calls except begin and end.

  7. For each Resolver Hook in H

    • Call the end method to inform the Handler about a resolve operation ending.

  8. For each Resolve Hook still in H, ensure that the Resolver Hook is no longer referenced.

In cases where the a shrinkable collection becomes empty the framework must continue calling the remaining hooks because these hooks can also be used to trace progress.

The above steps are meant to illustrate how the Resolve Hooks are used by the Resolver, they are not normative. The nature of the resolve operation and the Resolver algorithm can require back tracking by the Resolver implementation. It is therefore acceptable for the Resolver to call the Resolver Hook multiple times with similar or identical parameters during a single resolve operation. This is true for all methods except the begin and end methods that are only called once during a resolve operation.

A Resolver Hook must always return the same answer for the same set of inputs, that is, it must be stable.

53.3.1 Resolver Hook Limitations

Resolver hooks are system level components. Handlers must be careful not to create an unresolvable state which is very hard for a developer or a provisioner to diagnose. Resolver Hooks also must not be allowed to start another resolve operation, for example by starting a bundle or resolving bundles. The framework must detect this and throw an Illegal State Exception.

In cases where a Bundle Exception can be thrown, such as the Bundle start method, the Illegal State Exception must be the cause of the Bundle Exception and the Bundle Exception must be of type RESOLVE_ERROR. In cases where an exception cannot be propagated to a caller, for example during dynamic import resolving, a Framework Event of type ERROR must be published.

All hooks are treated as ordinary services, they can be used by the system bundle or other bundles.

53.3.2 Failures

If during the resolving anything goes wrong then the Resolver must abort the resolve operation, clean up and report an error.

If the begin(Collection) method successfully returns a ResolverHook, then the end() method must be called on that hook if it is still valid (its ResolverHookFactory is still registered) at the end of the Resolve. A ResolverHook can therefore safely allocate resources in the begin(Collection) method because it is guaranteed that it can deallocate them in the end() method.

The following are potential failures:

  • A Resolver Hook Factory used in a resolve operation is unregistered

  • A Resolver Hook throws an exception.

If the Resolver fails, it must throw a Bundle Exception to the caller if possible. Otherwise it must publish a Framework Event of type ERROR.

53.4 Security

The Resolver Hook Factory service described in this specification is a highly complex facility that requires great care in their implementation to maintain the Framework invariants. It is therefore important that in a secure system the permission to register these hooks is only given to privileged bundles.

In this case, the user of the Resolver Hook Factory service is the framework. Therefore, there is never a need to provide this service. Implementers of these hooks must have:

  • ServicePermission[..ResolverHookFactory,REGISTER] for Event Listener Hook services.

53.5 org.osgi.framework.hooks.resolver

Version 1.0

Framework Resolver Hooks Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest.

Example import for consumers using the API in this package:

Import-Package: org.osgi.framework.hooks.resolver; version="[1.0,2.0)"

53.5.1 Summary

53.5.2 public interface ResolverHook

OSGi Framework Resolver Hook instances are obtained from the OSGi Framework Resolver Hook Factory service.

A Resolver Hook instance is called by the framework during a resolve process. A resolver hook may influence the outcome of a resolve process by removing entries from shrinkable collections that are passed to the hook during a resolve process. A shrinkable collection is a Collection that supports all remove operations. Any other attempts to modify a shrinkable collection will result in an UnsupportedOperationException being thrown.

The following steps outline the way a framework uses the resolver hooks during a resolve process.

  1. Collect a snapshot of registered resolver hook factories that will be called during the current resolve process. Any hook factories registered after the snapshot is taken must not be called during the current resolve process. A resolver hook factory contained in the snapshot may become unregistered during the resolve process. The framework should handle this and stop calling the resolver hook instance provided by the unregistered hook factory and the current resolve process must fail. If possible, an exception must be thrown to the caller of the API which triggered the resolve process. In cases where the caller is not available a framework event of type error should be fired.

  2. For each registered hook factory call the ResolverHookFactory.begin(Collection) method to inform the hooks about a resolve process beginning and to obtain a Resolver Hook instance that will be used for the duration of the resolve process.

  3. Determine the collection of unresolved bundle revisions that may be considered for resolution during the current resolution process and place each of the bundle revisions in a shrinkable collection Resolvable. For each resolver hook call the filterResolvable(Collection) method with the shrinkable collection Resolvable.

  4. The shrinkable collection Resolvable now contains all the unresolved bundle revisions that may end up as resolved at the end of the current resolve process. Any other bundle revisions that got removed from the shrinkable collection Resolvable must not end up as resolved at the end of the current resolve process.

  5. For each bundle revision B left in the shrinkable collection Resolvable and any bundle revision B which is currently resolved that represents a singleton bundle do the following:

    • Determine the collection of available capabilities that have a namespace of osgi.identity, are singletons, and have the same symbolic name as the singleton bundle revision B and place each of the matching capabilities into a shrinkable collection Collisions.

    • Remove the osgi.identity capability provided by bundle revision B from shrinkable collection Collisions. A singleton bundle cannot collide with itself.

    • For each resolver hook call the filterSingletonCollisions(BundleCapability, Collection) with the osgi.identity capability provided by bundle revision B and the shrinkable collection Collisions

    • The shrinkable collection Collisions now contains all singleton osgi.identity capabilities that can influence the ability of bundle revision B to resolve.

    • If the bundle revision B is already resolved then any resolvable bundle revision contained in the collection Collisions is not allowed to resolve.

  6. During a resolve process a framework is free to attempt to resolve any or all bundles contained in shrinkable collection Resolvable. For each bundle revision B left in the shrinkable collection Resolvable which the framework attempts to resolve the following steps must be followed:

    • For each requirement R specified by bundle revision B determine the collection of capabilities that satisfy (or match) the requirement and place each matching capability into a shrinkable collection Candidates. A capability is considered to match a particular requirement if its attributes satisfy a specified requirement and the requirer bundle has permission to access the capability.

    • For each resolver hook call the filterMatches(BundleRequirement, Collection) with the requirement R and the shrinkable collection Candidates.

    • The shrinkable collection Candidates now contains all the capabilities that may be used to satisfy the requirement R. Any other capabilities that got removed from the shrinkable collection Candidates must not be used to satisfy requirement R.

  7. For each resolver hook call the end() method to inform the hooks about a resolve process ending.

In all cases, the order in which the resolver hooks are called is the reverse compareTo ordering of their Service References. That is, the service with the highest ranking number must be called first. In cases where a shrinkable collection becomes empty the framework is required to call the remaining registered hooks.

Resolver hooks are low level. Implementations of the resolver hook must be careful not to create an unresolvable state which is very hard for a developer or a provisioner to diagnose. Resolver hooks also must not be allowed to start another synchronous resolve process (e.g. by calling Bundle.start() or FrameworkWiring.resolveBundles(Collection) ). The framework must detect this and throw an IllegalStateException.

ResolverHookFactory

Not Thread-safe

53.5.2.1 public void end()

This method is called once at the end of the resolve process. After the end method is called the resolve process has ended. The framework must not hold onto this resolver hook instance after end has been called.

53.5.2.2 public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates)

the requirement to filter candidates for

a collection of candidates that match the requirement

Filter matches hook method. This method is called during the resolve process for the specified requirement. The collection of candidates match the specified requirement. This method can filter the collection of matching candidates by removing candidates from the collection. Removing a candidate will prevent the resolve process from choosing the removed candidate to satisfy the requirement.

All of the candidates will have the same namespace and will match the specified requirement.

If the Java Runtime Environment supports permissions then the collection of candidates will only contain candidates for which the requirer has permission to access.

53.5.2.3 public void filterResolvable(Collection<BundleRevision> candidates)

the collection of resolvable candidates available during a resolve process.

Filter resolvable candidates hook method. This method may be called multiple times during a single resolve process. This method can filter the collection of candidates by removing potential candidates. Removing a candidate will prevent the candidate from resolving during the current resolve process.

53.5.2.4 public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates)

the singleton involved in a resolve process

a collection of singleton collision candidates

Filter singleton collisions hook method. This method is called during the resolve process for the specified singleton. The specified singleton represents a singleton capability and the specified collection represent a collection of singleton capabilities which are considered collision candidates. The singleton capability and the collection of collision candidates must all use the same namespace.

Currently only capabilities with the namespace of osgi.wiring.bundle and osgi.identity can be singletons. The collision candidates will all have the same namespace, be singletons, and have the same symbolic name as the specified singleton capability.

In the future, capabilities in other namespaces may support the singleton concept. Hook implementations should be prepared to receive calls to this method for capabilities in namespaces other than osgi.wiring.bundle or osgi.identity.

This method can filter the list of collision candidates by removing potential collisions. Removing a collision candidate will allow the specified singleton to resolve regardless of the resolution state of the removed collision candidate.

53.5.3 public interface ResolverHookFactory

OSGi Framework Resolver Hook Factory Service.

Bundles registering this service will be called by the framework during a bundle resolver process to obtain a resolver hook instance which will be used for the duration of a resolve process.

ResolverHook

Thread-safe

53.5.3.1 public ResolverHook begin(Collection<BundleRevision> triggers)

an unmodifiable collection of bundles which triggered the resolve process. This collection may be empty if the collection of trigger bundles cannot be determined.

This method is called by the framework each time a resolve process begins to obtain a resolver hook instance. This resolver hook instance will be used for the duration of the resolve process. At the end of the resolve process the method ResolverHook.end() must be called by the framework and the framework must not hold any references of the resolver hook instance.

The triggers represent the collection of bundles which triggered the resolve process. This collection may be empty if the triggers cannot be determined by the framework. In most cases the triggers can easily be determined. Calling certain methods on bundle when a bundle is in the INSTALLED state will cause the framework to begin a resolve process in order to resolve the bundle. The following methods will start a resolve process in this case:

In such cases the collection will contain the single bundle which the framework is trying to resolve. Other cases will cause multiple bundles to be included in the trigger bundles collection. When resolveBundles is called the collection of triggers must include all the current bundle revisions for bundles passed to resolveBundles which are in the INSTALLED state.

When FrameworkWiring.refreshBundles(Collection, org.osgi.framework.FrameworkListener...) is called the collection of triggers is determined with the following steps:

  • If the collection of bundles passed is null then FrameworkWiring.getRemovalPendingBundles() is called to get the initial collection of bundles.

  • The equivalent of calling FrameworkWiring.getDependencyClosure(Collection) is called with the initial collection of bundles to get the dependency closure collection of the bundles being refreshed.

  • Remove any non-active bundles from the dependency closure collection.

  • For each bundle remaining in the dependency closure collection get the current bundle revision and add it to the collection of triggers.

As described above, a resolve process is typically initiated as a result of calling API that causes the framework to attempt to resolve one or more bundles. The framework is free to start a resolve process at any time for reasons other than calls to framework API. For example, a resolve process may be used by the framework for diagnostic purposes and result in no bundles actually becoming resolved at the end of the process. Resolver hook implementations must be prepared for resolve processes that are initiated for other reasons besides calls to framework API.

a resolver hook instance to be used for the duration of the resolve process. A null value may be returned which indicates this resolver hook factory abstains from the resolve process.