/*
 * Copyright (c) OSGi Alliance (2010, 2012). 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.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 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:
 * <p/>
 * 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}.
 * <p/>
 * 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.
 * <p/>
 * 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}
 * <p/>
 * 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.
 * <p/>
 * 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>
 * <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:
 * <p/>
 * 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.
 * 
 * <p/>
 * For each resolver hook call the
 * {@link #filterMatches(BundleRequirement, Collection)} with the requirement
 * {@code R} and the shrinkable collection {@code Candidates}.
 * 
 * <p/>
 * 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>
 * <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
 * @version $Id: 9d3ef6240aead0952b5a47b793780c1c0589089a $
 */
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();
}
