blob: 024abd009729e6feafab9fb7cdcc392e8755f069 [file] [log] [blame]
/*
* Copyright (c) OSGi Alliance (2010, 2018). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.osgi.framework.hooks.resolver;
import java.util.Collection;
import org.osgi.annotation.versioning.ConsumerType;
import org.osgi.framework.Bundle;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.FrameworkWiring;
/**
* OSGi Framework Resolver Hook instances are obtained from the OSGi
* {@link ResolverHookFactory Framework Resolver Hook Factory} service.
*
* <p>
* 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 {@code Collection} that
* supports all remove operations. Any other attempts to modify a shrinkable
* collection will result in an {@code UnsupportedOperationException} being
* thrown.
*
* <p>
* The following steps outline the way a framework uses the resolver hooks
* during a resolve process.
* <ol>
* <li>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.</li>
* <li>For each registered hook factory call the
* {@link 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.</li>
*
* <li>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 {@code Resolvable}.
* For each resolver hook call the {@link #filterResolvable(Collection)} method
* with the shrinkable collection {@code Resolvable}.</li>
* <li>The shrinkable collection {@code 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 {@code Resolvable} must not end up as resolved at the
* end of the current resolve process.</li>
* <li>For each bundle revision {@code B} left in the shrinkable collection
* {@code Resolvable} and any bundle revision {@code B} which is currently
* resolved that represents a singleton bundle do the following:
* <ul>
* <li>Determine the collection of available capabilities that have a namespace
* of {@link IdentityNamespace osgi.identity}, are singletons, and have the same
* symbolic name as the singleton bundle revision {@code B} and place each of
* the matching capabilities into a shrinkable collection {@code Collisions}.</li>
* <li>Remove the {@link IdentityNamespace osgi.identity} capability provided by
* bundle revision {@code B} from shrinkable collection {@code Collisions}. A
* singleton bundle cannot collide with itself.</li>
* <li>For each resolver hook call the
* {@link #filterSingletonCollisions(BundleCapability, Collection)} with the
* {@link IdentityNamespace osgi.identity} capability provided by bundle
* revision {@code B} and the shrinkable collection {@code Collisions}</li>
* <li>The shrinkable collection {@code Collisions} now contains all singleton
* {@link IdentityNamespace osgi.identity} capabilities that can influence the
* ability of bundle revision {@code B} to resolve.</li>
* <li>If the bundle revision {@code B} is already resolved then any resolvable
* bundle revision contained in the collection {@code Collisions} is not allowed
* to resolve.</li>
* </ul>
* </li>
* <li>During a resolve process a framework is free to attempt to resolve any or
* all bundles contained in shrinkable collection {@code Resolvable}. For each
* bundle revision {@code B} left in the shrinkable collection
* {@code Resolvable} which the framework attempts to resolve the following
* steps must be followed:
* <ul>
* <li>For each requirement {@code R} specified by bundle revision {@code B}
* determine the collection of capabilities that satisfy (or match) the
* requirement and place each matching capability into a shrinkable collection
* {@code 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.</li>
* <li>For each resolver hook call the
* {@link #filterMatches(BundleRequirement, Collection)} with the requirement
* {@code R} and the shrinkable collection {@code Candidates}.</li>
* <li>The shrinkable collection {@code Candidates} now contains all the
* capabilities that may be used to satisfy the requirement {@code R}. Any other
* capabilities that got removed from the shrinkable collection
* {@code Candidates} must not be used to satisfy requirement {@code R}.</li>
* </ul>
* </li>
* <li>For each resolver hook call the {@link #end()} method to inform the hooks
* about a resolve process ending.</li>
* </ol>
* 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.
* <p>
* 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
* {@link Bundle#start()} or {@link FrameworkWiring#resolveBundles(Collection)}
* ). The framework must detect this and throw an {@link IllegalStateException}.
*
* @see ResolverHookFactory
* @NotThreadSafe
* @author $Id: 6c1f3732d22b088e9f8a80c17e70e61862abf1c4 $
*/
@ConsumerType
public interface ResolverHook {
/**
* 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.
*
* @param candidates the collection of resolvable candidates available
* during a resolve process.
*/
void filterResolvable(Collection<BundleRevision> 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.
* <p>
* Currently only capabilities with the namespace of {@link BundleNamespace
* osgi.wiring.bundle} and {@link IdentityNamespace 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.
* <p>
* 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 {@link BundleNamespace
* osgi.wiring.bundle} or {@link IdentityNamespace osgi.identity}.
* <p>
* 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.
*
* @param singleton the singleton involved in a resolve process
* @param collisionCandidates a collection of singleton collision candidates
*/
void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates);
/**
* 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.
* <p>
* All of the candidates will have the same namespace and will match the
* specified requirement.
* <p>
* If the Java Runtime Environment supports permissions then the collection
* of candidates will only contain candidates for which the requirer has
* permission to access.
*
* @param requirement the requirement to filter candidates for
* @param candidates a collection of candidates that match the requirement
*/
void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates);
/**
* 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.
*/
void end();
}