![JAR search and dependency download from the Maven repository](/logo.png)
biz.aQute.resolve.BndrunResolveContext Maven / Gradle / Ivy
Show all versions of biz.aQute.bnd Show documentation
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.jar.Manifest;
import org.osgi.framework.Version;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
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 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.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.Strategy;
import aQute.bnd.service.resolve.hook.ResolverHook;
import aQute.lib.converter.Converter;
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 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 Registry registry;
private Parameters resolvePrefs;
private final Processor properties;
private Project project;
private boolean initialized;
/**
* 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;
initialized = true;
try {
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);
} 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));
system.addExportPackages(systemPackages);
//
// We make the system capabilities as coming from the system
// resource
//
Parameters systemCapabilities = new Parameters(
properties.mergeProperties(Constants.RUNSYSTEMCAPABILITIES));
system.addProvideCapabilities(systemCapabilities);
//
// 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));
system.addProvideCapabilities(providedCapabilities);
//
// 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
//
//
// TODO Add osgi.wiring.bundle + osgi.wiring.host
// filed a bug about using the impl version for the system
// capabilities
//
List frameworkPackages = system.findCapabilities(PackageNamespace.PACKAGE_NAMESPACE,
"(" + PackageNamespace.PACKAGE_NAMESPACE + "=org.osgi.framework)");
if (!frameworkPackages.isEmpty()) {
Capability c = frameworkPackages.get(0);
Version version = (Version) c.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
CapReqBuilder crb = new CapReqBuilder(IdentityNamespace.IDENTITY_NAMESPACE);
crb.addAttribute(IdentityNamespace.IDENTITY_NAMESPACE, "system.bundle");
crb.addAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
system.addCapability(crb);
}
setSystemResource(system.build());
} catch (Exception e) {
log.log(LogService.LOG_ERROR, e.getMessage(), e);
throw new RuntimeException(e);
}
super.init();
}
void loadFramework(ResourceBuilder system) throws Exception {
Parameters parameters = new Parameters(properties.getProperty(Constants.RUNFW));
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.
*/
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) {
Manifest manifest = c.getManifest();
if (manifest != null) {
ResourceBuilder rb = new ResourceBuilder();
rb.addManifest(Domain.domain(manifest));
addSystemResource(system, 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).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
*
* TODO Use Instruction ...
*
* @return
* @throws Exception
*/
private Processor loadRepositories() throws Exception {
//
// Get all of the repositories from the plugin registry
//
List allRepos = registry.getPlugins(Repository.class);
Collection orderedRepositories;
String rn = properties.mergeProperties(Constants.RUNREPOS);
if (rn == null) {
//
// No filter set, so we use all
//
orderedRepositories = allRepos;
} else {
Parameters repoNames = new Parameters(rn);
// Map the repository names...
Map repoNameMap = new HashMap(allRepos.size());
for (Repository repo : allRepos)
repoNameMap.put(repo.toString(), 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));
augments.putAll(new Parameters(properties.mergeProperties(Constants.AUGMENT)));
if (!augments.isEmpty()) {
AggregateRepository aggregate = new AggregateRepository(orderedRepositories);
AugmentRepository augment = new AugmentRepository(augments, aggregate);
orderedRepositories = Collections.singleton((Repository) augment);
}
for (Repository repository : orderedRepositories) {
super.addRepository(repository);
}
return repositoryAugments;
}
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 {
project.trace("loading augments from %s", 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);
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);
resBuilder.addCapability(identity);
Parameters inputRequirements = new Parameters(properties.mergeProperties(Constants.RUNREQUIRES));
if (inputRequirements != null && !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));
blacklist.putAll(new Parameters(properties.mergeProperties(Constants.RUNBLACKLIST)));
if (blacklist != null && !blacklist.isEmpty()) {
List reject = CapReqBuilder.getRequirementsFrom(blacklist);
setBlackList(reject);
}
}
private void loadPreferences() {
resolvePrefs = new Parameters(properties.getProperty(PROP_RESOLVE_PREFERENCES));
}
protected void postProcessProviders(Requirement requirement, Set wired, List candidates) {
if (candidates.size() == 0)
return;
// Call resolver hooks
for (ResolverHook resolverHook : registry.getPlugins(ResolverHook.class)) {
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);
}
}
}
}