All Downloads are FREE. Search and download functionalities are using the official Maven repository.

biz.aQute.resolve.BndrunResolveContext Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package biz.aQute.resolve;

import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.jar.Manifest;

import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.service.log.LogService;
import org.osgi.service.repository.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import aQute.bnd.build.Container;
import aQute.bnd.build.Project;
import aQute.bnd.build.model.BndEditModel;
import aQute.bnd.build.model.EE;
import aQute.bnd.header.Attrs;
import aQute.bnd.header.Parameters;
import aQute.bnd.http.HttpClient;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Domain;
import aQute.bnd.osgi.Jar;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.repository.AggregateRepository;
import aQute.bnd.osgi.repository.AugmentRepository;
import aQute.bnd.osgi.repository.WorkspaceRepositoryMarker;
import aQute.bnd.osgi.resource.CapReqBuilder;
import aQute.bnd.osgi.resource.RequirementBuilder;
import aQute.bnd.osgi.resource.ResourceBuilder;
import aQute.bnd.osgi.resource.ResourceUtils;
import aQute.bnd.service.Registry;
import aQute.bnd.service.RepositoryPlugin;
import aQute.bnd.service.Strategy;
import aQute.bnd.service.resolve.hook.ResolverHook;
import aQute.lib.converter.Converter;
import aQute.lib.strings.Strings;
import aQute.lib.utf8properties.UTF8Properties;

/**
 * This class does the resolving for bundles. It loads the details from a
 * BndEditModel & Project
 */
public class BndrunResolveContext extends AbstractResolveContext {
	private final static Logger			logger						= LoggerFactory
		.getLogger(BndrunResolveContext.class);

	private static final String			BND_AUGMENT					= "bnd.augment";
	public static final String			RUN_EFFECTIVE_INSTRUCTION	= "-resolve.effective";
	public static final String			PROP_RESOLVE_PREFERENCES	= "-resolve.preferences";
	private static final String			NAMESPACE_WHITELIST			= "x-whitelist";

	private Registry					registry;
	private Parameters					resolvePrefs;
	private final Processor				properties;
	private Project						project;
	private boolean						initialized;
	private volatile List	resolverHooks;

	/**
	 * Constructor for a BndEditModel. The idea to use a BndEditModel was rather
	 * bad because it couples things that should not be coupled. The other
	 * constructor should be preferred.
	 *
	 * @param runModel The edit model
	 * @param registry The bnd registry
	 * @param log
	 */
	@Deprecated
	public BndrunResolveContext(BndEditModel runModel, Registry registry, LogService log) {
		super(log);
		try {
			this.registry = registry;
			this.properties = runModel.getProperties();
			this.project = runModel.getProject();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * The preferred constructor
	 *
	 * @param runModel The model (its properties)
	 * @param project The project to access bundles
	 * @param registry the registry
	 * @param log
	 */

	public BndrunResolveContext(Processor runModel, Project project, Registry registry, LogService log) {
		super(log);
		try {
			this.registry = registry;
			this.properties = runModel;
			this.project = project;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Initializes the resolver. Here we will load all the information from the
	 * model.
	 */
	@Override
	public synchronized void init() {

		if (initialized)
			return;

		try {
			initialized = true;

			if (getLevel() <= 0) {
				Integer level = Converter.cnv(Integer.class, properties.getProperty("-resolvedebug", "0"));
				if (level != null)
					setLevel(level);
			}

			loadPreferences();

			Processor augments = loadRepositories();

			constructBlacklist(augments);

			Map> effectiveSet = loadEffectiveSet();
			if (effectiveSet != null)
				addEffectiveSet(effectiveSet);

			//
			// Create a resource from the -runrequire that contains
			// all the requirement
			//

			setInputResource(constructInputRequirements());

			//
			// We gradually build up the system resource that contains
			// the system packages, the EE, etc.
			//

			ResourceBuilder system = new ResourceBuilder();

			//
			// Let's identify the system resource to make it look less
			// ugly
			//

			//
			// If we have a distro, we do not load the environment
			// settings
			//

			String distro = properties.mergeProperties(Constants.DISTRO);
			if (distro != null && !distro.trim()
				.isEmpty()) {
				loadPath(system, distro, Constants.DISTRO);

				loadProvidedCapabilities(system);
			} else {
				//
				// Load the EE's and packages that belong to it.
				//

				EE tmp = EE.parse(properties.getProperty(Constants.RUNEE));
				EE ee = (tmp != null) ? tmp : EE.JavaSE_1_6;

				system.addAllExecutionEnvironments(ee);

				//
				// We make the system packages as coming from the system
				// resource
				//

				Parameters systemPackages = new Parameters(properties.mergeProperties(Constants.RUNSYSTEMPACKAGES),
					project);
				system.addExportPackages(systemPackages);

				//
				// We make the system capabilities as coming from the system
				// resource
				//

				Parameters systemCapabilities = new Parameters(
					properties.mergeProperties(Constants.RUNSYSTEMCAPABILITIES), project);
				system.addProvideCapabilities(systemCapabilities);

				loadProvidedCapabilities(system);

				//
				// Load the frameworks capabilities
				//

				loadFramework(system);

				//
				// Analyze the path and add all exported packages and provided
				// capabilities
				// to the system resource
				//

				String runpath = properties.mergeProperties(Constants.RUNPATH);

				if (runpath != null && !runpath.trim()
					.isEmpty())
					loadPath(system, runpath, Constants.RUNPATH);
			}

			//
			// We've not gathered all the capabilities of the system
			// so we can create the resource and set it as the system resource
			//

			setSystemResource(system.build());
		} catch (Exception e) {
			log.log(LogService.LOG_ERROR, e.getMessage(), e);
			throw new RuntimeException(e);
		}
		super.init();
	}

	private void loadProvidedCapabilities(ResourceBuilder system) throws Exception {
		//
		// Some capabilities are provided by the runtime, like native
		// code.
		// We need to add them here so the resolver is aware of them
		//

		Parameters providedCapabilities = new Parameters(properties.mergeProperties(Constants.RUNPROVIDEDCAPABILITIES),
			project);
		system.addProvideCapabilities(providedCapabilities);
	}

	void loadFramework(ResourceBuilder system) throws Exception {
		Parameters parameters = new Parameters(properties.getProperty(Constants.RUNFW), project);
		if (parameters.isEmpty()) {
			log.log(LogService.LOG_WARNING, "No -runfw set");
			return;
		}

		if (parameters.size() > 1)
			throw new IllegalArgumentException(
				"Too many frameworks specified in " + Constants.RUNFW + " (" + parameters + ")");

		Map.Entry bsn = parameters.entrySet()
			.iterator()
			.next();

		String name = bsn.getKey();
		String version = bsn.getValue()
			.getVersion();

		log.log(LogService.LOG_INFO, "Using frameowork " + name + ";" + version);

		if ("none".equals(name))
			return;

		Resource framework = getHighestResource(name, version);
		if (framework == null) {
			log.log(LogService.LOG_ERROR, "Cannot find framework " + name + ";" + version);
		} else
			super.setFramework(system, framework);

	}

	/**
	 * Add a path to the system resource. This is done by the bnd launcher for
	 * -runpath and it is also used for -distro.
	 */

	@Override
	public void loadPath(ResourceBuilder system, String path, String what) throws Exception {

		if (project != null) {
			List containers = Container.flatten(project.getBundles(Strategy.HIGHEST, path, what));

			for (Container c : containers) {
				HashSet ignoredNamespaces = new HashSet<>(IGNORED_NAMESPACES_FOR_SYSTEM_RESOURCES);
				Strings.splitAsStream(c.getAttributes()
					.get(NAMESPACE_WHITELIST))
					.forEach(ignoredNamespaces::remove);

				Manifest manifest = c.getManifest();
				if (manifest != null) {
					ResourceBuilder rb = new ResourceBuilder();
					rb.addManifest(Domain.domain(manifest));
					system.copyCapabilities(ignoredNamespaces, rb.build());
				}

			}
		} else {
			super.loadPath(system, path, what);
		}
	}

	/**
	 * Load the effective set from the properties
	 */
	Map> loadEffectiveSet() {
		String effective = properties.getProperty(RUN_EFFECTIVE_INSTRUCTION);
		if (effective == null)
			return null;

		HashMap> effectiveSet = new HashMap<>();

		for (Entry entry : new Parameters(effective, project).entrySet()) {
			String skip = entry.getValue()
				.get("skip:");
			Set toSkip = skip == null ? new HashSet<>() : new HashSet<>(Arrays.asList(skip.split(",")));
			effectiveSet.put(entry.getKey(), toSkip);
		}

		return effectiveSet;
	}

	/**
	 * Load all the OSGi repositories from our registry
	 * 

* * @return * @throws Exception */ private Processor loadRepositories() throws Exception { // // Get all of the repositories from the plugin registry // List allRepos = getAllRepos(); Collection orderedRepositories; String runRepos = properties.mergeProperties(Constants.RUNREPOS); if (runRepos == null) { // // No filter set, so we use all // orderedRepositories = allRepos; } else { Parameters repoNames = new Parameters(runRepos, project); // Map the repository names... Map repoNameMap = new HashMap<>(allRepos.size()); for (Repository repo : allRepos) { String name; if (repo instanceof RepositoryPlugin) { name = ((RepositoryPlugin) repo).getName(); } else { name = repo.toString(); } repoNameMap.put(name, repo); } // Create the result list orderedRepositories = new ArrayList<>(); for (String repoName : repoNames.keySet()) { Repository repo = repoNameMap.get(repoName); if (repo != null) orderedRepositories.add(repo); } } Processor repositoryAugments = findRepositoryAugments(orderedRepositories); Parameters augments = new Parameters(repositoryAugments.mergeProperties(Constants.AUGMENT), project); augments.putAll(new Parameters(properties.mergeProperties(Constants.AUGMENT), project)); if (!augments.isEmpty()) { AggregateRepository aggregate = new AggregateRepository(orderedRepositories); AugmentRepository augment = new AugmentRepository(augments, aggregate); orderedRepositories = Collections.singleton(augment); } for (Repository repository : orderedRepositories) { super.addRepository(repository); } return repositoryAugments; } private List getAllRepos() { List allRepos; if (project != null && !project.isStandalone() ) { allRepos = project.getWorkspace().getPlugins(Repository.class); allRepos.removeIf( WorkspaceRepositoryMarker.class::isInstance); WorkspaceResourcesRepository wr = new WorkspaceResourcesRepository(project.getWorkspace()); allRepos.add(wr); } else { allRepos = registry.getPlugins(Repository.class); } return allRepos; } private Processor findRepositoryAugments(Collection orderedRepositories) { Processor main = new Processor(); RequirementBuilder rb = new RequirementBuilder(BND_AUGMENT); rb.filter("(path=*)"); Requirement req = rb.buildSyntheticRequirement(); for (Repository r : orderedRepositories) { Map> found = r.findProviders(Collections.singleton(req)); Collection capabilities = found.get(req); if (capabilities != null) { for (Capability capability : capabilities) { findAdditionalAugmentsFromResource(main, capability); } } } return main; } private void findAdditionalAugmentsFromResource(Processor augments, Capability capability) { Resource resource = capability.getResource(); Map locations = ResourceUtils.getLocations(resource); if (locations == null || locations.isEmpty()) return; Object pathObject = capability.getAttributes() .get("path"); if (pathObject == null) pathObject = "augments.bnd"; if (pathObject instanceof String) { String path = (String) pathObject; HttpClient http = registry.getPlugin(HttpClient.class); for (URI uri : locations.keySet()) try { logger.debug("loading augments from {}", uri); File file = http.build() .age(24, TimeUnit.HOURS) .useCache() .go(uri); try (Jar jar = new Jar(file)) { aQute.bnd.osgi.Resource rs = jar.getResource(path); try (InputStream in = rs.openInputStream()) { UTF8Properties p = new UTF8Properties(); p.load(in, file, project, Constants.OSGI_SYNTAX_HEADERS); augments.getProperties() .putAll(p); return; } } } catch (Exception e) { project.warning("Failed to handle augment resource from repo %s", uri); } } } @Override public boolean isSystemResource(Resource resource) { Resource systemResource = getSystemResource(); return resource == systemResource; } Resource constructInputRequirements() throws Exception { ResourceBuilder resBuilder = new ResourceBuilder(); CapReqBuilder identity = new CapReqBuilder(IdentityNamespace.IDENTITY_NAMESPACE) .addAttribute(IdentityNamespace.IDENTITY_NAMESPACE, IDENTITY_INITIAL_RESOURCE) .addAttribute(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, IdentityNamespace.TYPE_BUNDLE); resBuilder.addCapability(identity); Parameters inputRequirements = new Parameters(properties.mergeProperties(Constants.RUNREQUIRES), project); if (!inputRequirements.isEmpty()) { List requires = CapReqBuilder.getRequirementsFrom(inputRequirements); resBuilder.addRequirements(requires); } return resBuilder.build(); } private void constructBlacklist(Processor augments) throws Exception { Parameters blacklist = new Parameters(augments.mergeProperties(Constants.RUNBLACKLIST), project); blacklist.putAll(new Parameters(properties.mergeProperties(Constants.RUNBLACKLIST), project)); if (!blacklist.isEmpty()) { List reject = CapReqBuilder.getRequirementsFrom(blacklist); setBlackList(reject); } } private void loadPreferences() { resolvePrefs = new Parameters(properties.getProperty(PROP_RESOLVE_PREFERENCES), project); } // Remove any capabilities that come from resources whose osgi.identity // capability is not type=osgi.bundle or type=osgi.fragment private static final ResolverHook bundleTypeResolverHook; static { Predicate> filterPredicate = ResourceUtils .filterPredicate("(|(" + IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE + "=" + IdentityNamespace.TYPE_BUNDLE + ")(" + IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE + "=" + IdentityNamespace.TYPE_FRAGMENT + "))"); Predicate capabilityPredicate = capability -> ResourceUtils .capabilityStream(capability.getResource(), IdentityNamespace.IDENTITY_NAMESPACE) .map(Capability::getAttributes) .noneMatch(filterPredicate); bundleTypeResolverHook = (requirement, capabilities) -> capabilities.removeIf(capabilityPredicate); } private List getResolverHooks() { if (resolverHooks != null) { return resolverHooks; } List hooks = registry.getPlugins(ResolverHook.class); hooks.add(bundleTypeResolverHook); return resolverHooks = hooks; } @Override protected void postProcessProviders(Requirement requirement, Set wired, List candidates) { if (candidates.isEmpty()) return; // Call resolver hooks for (ResolverHook resolverHook : getResolverHooks()) { resolverHook.filterMatches(requirement, candidates); } // Process the resolve preferences boolean prefsUsed = false; if (resolvePrefs != null && !resolvePrefs.isEmpty()) { List insertions = new LinkedList<>(); for (Iterator iterator = candidates.iterator(); iterator.hasNext();) { Capability cap = iterator.next(); if (resolvePrefs.containsKey(getResourceIdentity(cap.getResource()))) { iterator.remove(); insertions.add(cap); } } if (!insertions.isEmpty()) { candidates.addAll(0, insertions); prefsUsed = true; } } // If preferences were applied, then don't need to call the callbacks if (!prefsUsed) { for (ResolutionCallback callback : getCallbacks()) { callback.processCandidates(requirement, wired, candidates); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy