aQute.bnd.osgi.resource.ResourceBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
package aQute.bnd.osgi.resource;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.NativeNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.namespace.service.ServiceNamespace;
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.service.repository.ContentNamespace;
import aQute.bnd.build.model.EE;
import aQute.bnd.header.Attrs;
import aQute.bnd.header.OSGiHeader;
import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Domain;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Verifier;
import aQute.bnd.version.VersionRange;
import aQute.lib.converter.Converter;
import aQute.lib.filter.Filter;
import aQute.libg.cryptography.SHA256;
import aQute.libg.reporter.ReporterAdapter;
import aQute.service.reporter.Reporter;
public class ResourceBuilder {
private final ResourceImpl resource = new ResourceImpl();
private final List capabilities = new LinkedList();
private final List requirements = new LinkedList();
private ReporterAdapter reporter = new ReporterAdapter();
private boolean built = false;
public ResourceBuilder(Resource source) throws Exception {
addCapabilities(source.getCapabilities(null));
addRequirements(source.getRequirements(null));
}
public ResourceBuilder() {}
public ResourceBuilder addCapability(Capability capability) throws Exception {
CapReqBuilder builder = CapReqBuilder.clone(capability);
return addCapability(builder);
}
public ResourceBuilder addCapability(CapReqBuilder builder) {
if (built)
throw new IllegalStateException("Resource already built");
addCapability0(builder);
return this;
}
public Capability addCapability0(CapReqBuilder builder) {
Capability cap = builder.setResource(resource).buildCapability();
capabilities.add(cap);
return cap;
}
public ResourceBuilder addRequirement(Requirement requirement) throws Exception {
if (requirement == null)
return this;
CapReqBuilder builder = CapReqBuilder.clone(requirement);
return addRequirement(builder);
}
public ResourceBuilder addRequirement(CapReqBuilder builder) {
if (builder == null)
return this;
if (built)
throw new IllegalStateException("Resource already built");
Requirement req = builder.setResource(resource).buildRequirement();
requirements.add(req);
return this;
}
public Resource build() {
if (built)
throw new IllegalStateException("Resource already built");
built = true;
resource.setCapabilities(capabilities);
resource.setRequirements(requirements);
return resource;
}
public List getCapabilities() {
return capabilities;
}
/**
* Parse the manifest and turn them into requirements & capabilities
*
* @param manifest The manifest to parse
* @throws Exception
*/
public boolean addManifest(Domain manifest) throws Exception {
//
// Do the Bundle Identity Ns
//
int bundleManifestVersion = Integer.parseInt(manifest.get(Constants.BUNDLE_MANIFESTVERSION, "1"));
Entry bsn = manifest.getBundleSymbolicName();
if (bsn == null) {
reporter.warning("No BSN set, not a bundle");
return false;
}
boolean singleton = "true".equals(bsn.getValue().get(Constants.SINGLETON_DIRECTIVE + ":"));
boolean fragment = manifest.getFragmentHost() != null;
String versionString = manifest.getBundleVersion();
if (versionString == null)
versionString = "0";
else if (!aQute.bnd.version.Version.isVersion(versionString))
throw new IllegalArgumentException("Invalid version in bundle " + bsn + ": " + versionString);
aQute.bnd.version.Version version = aQute.bnd.version.Version.parseVersion(versionString);
//
// First the identity
//
CapReqBuilder identity = new CapReqBuilder(resource, IdentityNamespace.IDENTITY_NAMESPACE);
identity.addAttribute(IdentityNamespace.IDENTITY_NAMESPACE, bsn.getKey());
identity.addAttribute(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
fragment ? IdentityNamespace.TYPE_FRAGMENT : IdentityNamespace.TYPE_BUNDLE);
identity.addAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
if ("true".equals(singleton)) {
identity.addDirective(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE, "true");
}
String copyright = manifest.get(Constants.BUNDLE_COPYRIGHT);
if (copyright != null) {
identity.addAttribute(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE, copyright);
}
String description = manifest.get(Constants.BUNDLE_DESCRIPTION);
if (description != null) {
identity.addAttribute(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE, description);
}
String docurl = manifest.get(Constants.BUNDLE_DOCURL);
if (docurl != null) {
identity.addAttribute(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE, docurl);
}
String license = manifest.get("Bundle-License");
if (license != null) {
identity.addAttribute(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE, license);
}
addCapability(identity.buildCapability());
//
// Now the provide bundle ns
//
if (bundleManifestVersion >= 2) {
CapReqBuilder provideBundle = new CapReqBuilder(resource, BundleNamespace.BUNDLE_NAMESPACE);
provideBundle.addAttributesOrDirectives(bsn.getValue());
provideBundle.addAttribute(BundleNamespace.BUNDLE_NAMESPACE, bsn.getKey());
provideBundle.addAttribute(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, version);
addCapability(provideBundle.buildCapability());
}
//
// Import/Export service
//
@SuppressWarnings("deprecation")
Parameters importServices = OSGiHeader.parseHeader(manifest.get(Constants.IMPORT_SERVICE));
addImportServices(importServices);
@SuppressWarnings("deprecation")
Parameters exportServices = OSGiHeader.parseHeader(manifest.get(Constants.EXPORT_SERVICE));
addExportServices(exportServices);
//
// Handle Require Bundle
//
Parameters requireBundle = manifest.getRequireBundle();
addRequireBundles(requireBundle);
//
// Handle Fragment Host
//
if (fragment) {
Entry fragmentHost = manifest.getFragmentHost();
addFragmentHost(fragmentHost.getKey(), fragmentHost.getValue());
}
else {
addFragmentHostCap(bsn.getKey(), version);
}
//
// Add the exported package. These need
// to be converted to osgi.wiring.package ns
//
addExportPackages(manifest.getExportPackage());
//
// Add the imported package. These need
// to be converted to osgi.wiring.package ns
//
addImportPackages(manifest.getImportPackage());
//
// Add the provided capabilities, they're easy!
//
addProvideCapabilities(manifest.getProvideCapability());
//
// Add the required capabilities, they're also easy!
//
addRequireCapabilities(manifest.getRequireCapability());
//
// Manage native code header
//
addRequirement(getNativeCode(manifest.getBundleNative()));
return true;
}
public void addExportServices(Parameters exportServices) throws Exception {
for (Map.Entry e : exportServices.entrySet()) {
String service = Processor.removeDuplicateMarker(e.getKey());
CapabilityBuilder cb = new CapabilityBuilder(ServiceNamespace.SERVICE_NAMESPACE);
cb.addAttributesOrDirectives(e.getValue());
cb.addAttribute(Constants.OBJECTCLASS, service);
addCapability(cb);
}
}
public void addImportServices(Parameters importServices) {
for (Map.Entry e : importServices.entrySet()) {
String service = Processor.removeDuplicateMarker(e.getKey());
boolean optional = Constants.RESOLUTION_OPTIONAL.equals(e.getValue().get("availability:"));
boolean multiple = "true".equalsIgnoreCase(e.getValue().get("multiple:"));
StringBuilder filter = new StringBuilder();
filter.append('(').append(Constants.OBJECTCLASS).append('=').append(service).append(')');
RequirementBuilder rb = new RequirementBuilder(ServiceNamespace.SERVICE_NAMESPACE);
rb.addFilter(filter.toString());
rb.addDirective("effective", "active");
if (optional)
rb.addDirective(ServiceNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
rb.addDirective(ServiceNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE,
multiple ? ServiceNamespace.CARDINALITY_MULTIPLE : ServiceNamespace.CARDINALITY_SINGLE);
addRequirement(rb);
}
}
/**
* Caclulate the requirement from a native code header
*
* @param header the Bundle-NativeCode header or null
* @return a Requirement Builder set to the requirements according tot he
* core spec
*/
public RequirementBuilder getNativeCode(String header) throws Exception {
if (header == null || header.isEmpty())
return null;
Parameters bundleNative = OSGiHeader.parseHeader(header, null, new Parameters(true));
if (bundleNative.isEmpty())
return null;
boolean optional = false;
List options = new LinkedList();
RequirementBuilder rb = new RequirementBuilder(NativeNamespace.NATIVE_NAMESPACE);
FilterBuilder sb = new FilterBuilder();
sb.or();
for (Entry entry : bundleNative.entrySet()) {
String name = Processor.removeDuplicateMarker(entry.getKey());
if ("*".equals(name)) {
optional = true;
continue;
}
sb.and();
/*
* • osname - Name of the operating system. The value of this
* attribute must be the name of the operating system upon which the
* native libraries run. A number of canonical names are defined in
* Table 4.3.
*/
doOr(sb, "osname", NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE, entry.getValue());
/*
* • processor - The processor architecture. The value of this
* attribute must be the name of the processor architecture upon
* which the native libraries run. A number of canonical names are
* defined in Table 4.2.
*/
doOr(sb, "processor", NativeNamespace.CAPABILITY_PROCESSOR_ATTRIBUTE, entry.getValue());
/*
* • language - The ISO code for a language. The value of this
* attribute must be the name of the language for which the native
* libraries have been localized.
*/
doOr(sb, "language", NativeNamespace.CAPABILITY_LANGUAGE_ATTRIBUTE, entry.getValue());
for (String key : entry.getValue().keySet()) {
Object value = entry.getValue().getTyped(key);
key = Processor.removeDuplicateMarker(key);
switch (key) {
case "osname" :
case "processor" :
case "language" :
break;
/*
* • osversion - The operating system version. The value of
* this attribute must be a version range as defined in
* Version Ranges on page 36.
*/
case "osversion" :
sb.eq(NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE, value);
break;
/*
* • selection-filter - A selection filter. The value of
* this attribute must be a filter expression that in-
* dicates if the native code clause should be selected or
* not.
*/
case "selection-filter" :
String filter = value.toString();
String validateFilter = Verifier.validateFilter(filter);
if (validateFilter != null) {
reporter.error("Invalid 'selection-filter' on Bundle-NativeCode %s", filter);
}
sb.literal(value.toString());
break;
default :
reporter.warning("Unknown attribute on Bundle-NativeCode header %s=%s", key, value);
break;
}
}
sb.endAnd();
}
sb.endOr();
if (optional)
rb.addDirective("resolution", "optional");
rb.addFilter(sb.toString());
return rb;
}
private static void doOr(FilterBuilder sb, String key, String attribute, Attrs attrs) throws Exception {
sb.or();
while (attrs.containsKey(key)) {
String[] names = Converter.cnv(String[].class, attrs.getTyped(key));
for (String name : names) {
sb.eq(attribute, name);
}
key += "~";
}
sb.endOr();
}
/**
* Add the Require-Bundle header
*
* @throws Exception
*/
public void addRequireBundles(Parameters requireBundle) throws Exception {
for (Entry clause : requireBundle.entrySet()) {
addRequireBundle(Processor.removeDuplicateMarker(clause.getKey()), clause.getValue());
}
}
public void addRequireBundle(String bsn, VersionRange range) throws Exception {
Attrs attrs = new Attrs();
attrs.put("bundle-version", range.toString());
addRequireBundle(bsn, attrs);
}
public void addRequireBundle(String bsn, Attrs attrs) throws Exception {
CapReqBuilder rbb = new CapReqBuilder(resource, BundleNamespace.BUNDLE_NAMESPACE);
rbb.addDirectives(attrs);
StringBuilder filter = new StringBuilder();
filter.append("(").append(BundleNamespace.BUNDLE_NAMESPACE).append("=").append(bsn).append(")");
String v = attrs.get(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
if (v != null && VersionRange.isOSGiVersionRange(v)) {
VersionRange range = VersionRange.parseOSGiVersionRange(v);
filter.insert(0, "(&");
filter.append(toBundleVersionFilter(range));
filter.append(")");
}
rbb.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
addRequirement(rbb.buildRequirement());
}
Object toBundleVersionFilter(VersionRange range) {
return range.toFilter().replaceAll(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
}
void addFragmentHostCap(String bsn, aQute.bnd.version.Version version) throws Exception {
CapReqBuilder rbb = new CapReqBuilder(resource, HostNamespace.HOST_NAMESPACE);
rbb.addAttribute(HostNamespace.HOST_NAMESPACE, bsn);
rbb.addAttribute(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, version);
addCapability(rbb.buildCapability());
}
public void addFragmentHost(String bsn, Attrs attrs) throws Exception {
CapReqBuilder rbb = new CapReqBuilder(resource, HostNamespace.HOST_NAMESPACE);
rbb.addDirectives(attrs);
StringBuilder filter = new StringBuilder();
filter.append("(").append(HostNamespace.HOST_NAMESPACE).append("=").append(bsn).append(")");
String v = attrs.getVersion();
if (v != null && VersionRange.isOSGiVersionRange(v)) {
VersionRange range = VersionRange.parseOSGiVersionRange(v);
filter.insert(0, "(&");
filter.append(range.toFilter());
filter.append(")");
}
rbb.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
addRequirement(rbb.buildRequirement());
}
public void addRequireCapabilities(Parameters required) throws Exception {
for (Entry clause : required.entrySet()) {
String namespace = Processor.removeDuplicateMarker(clause.getKey());
addRequireCapability(namespace, Processor.removeDuplicateMarker(clause.getKey()), clause.getValue());
}
}
public void addRequireCapability(String namespace, String name, Attrs attrs) throws Exception {
CapReqBuilder req = new CapReqBuilder(resource, namespace);
req.addAttributesOrDirectives(attrs);
addRequirement(req.buildRequirement());
}
public List addProvideCapabilities(Parameters capabilities) throws Exception {
List added = new ArrayList<>();
for (Entry clause : capabilities.entrySet()) {
String namespace = Processor.removeDuplicateMarker(clause.getKey());
Attrs attrs = clause.getValue();
Capability addedCapability = addProvideCapability(namespace, attrs);
added.add(addedCapability);
}
return added;
}
public List addProvideCapabilities(String clauses) throws Exception {
return addProvideCapabilities(new Parameters(clauses));
}
public Capability addProvideCapability(String namespace, Attrs attrs) throws Exception {
CapReqBuilder capb = new CapReqBuilder(resource, namespace);
capb.addAttributesOrDirectives(attrs);
return addCapability0(capb);
}
/**
* Add Exported Packages
*
* @throws Exception
*/
public void addExportPackages(Parameters exports) throws Exception {
for (Entry clause : exports.entrySet()) {
String pname = Processor.removeDuplicateMarker(clause.getKey());
Attrs attrs = clause.getValue();
addExportPackage(pname, attrs);
}
}
public void addEE(EE ee) throws Exception {
addExportPackages(ee.getPackages());
EE[] compatibles = ee.getCompatible();
addExecutionEnvironment(ee);
for (EE compatible : compatibles) {
addExecutionEnvironment(compatible);
}
}
public void addExportPackage(String packageName, Attrs attrs) throws Exception {
CapReqBuilder capb = new CapReqBuilder(resource, PackageNamespace.PACKAGE_NAMESPACE);
capb.addAttributesOrDirectives(attrs);
if (!attrs.containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE)) {
capb.addAttribute(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, Version.emptyVersion);
}
capb.addAttribute(PackageNamespace.PACKAGE_NAMESPACE, packageName);
addCapability(capb);
}
/**
* Add imported packages
*
* @throws Exception
*/
public void addImportPackages(Parameters imports) throws Exception {
for (Entry clause : imports.entrySet()) {
String pname = Processor.removeDuplicateMarker(clause.getKey());
Attrs attrs = clause.getValue();
addImportPackage(pname, attrs);
}
}
public Requirement addImportPackage(String pname, Attrs attrs) throws Exception {
CapReqBuilder reqb = new CapReqBuilder(resource, PackageNamespace.PACKAGE_NAMESPACE);
reqb.addDirectives(attrs);
reqb.addFilter(PackageNamespace.PACKAGE_NAMESPACE, pname, attrs.getVersion(), attrs);
Requirement requirement = reqb.buildRequirement();
addRequirement(requirement);
return requirement;
}
// Correct version according to R5 specification section 3.4.1
// BREE J2SE-1.4 ==> osgi.ee=JavaSE, version:Version=1.4
// See bug 329, https://github.com/bndtools/bnd/issues/329
public void addExecutionEnvironment(EE ee) throws Exception {
CapReqBuilder builder = new CapReqBuilder(resource,
ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
builder.addAttribute(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, ee.getCapabilityName());
builder.addAttribute(ExecutionEnvironmentNamespace.CAPABILITY_VERSION_ATTRIBUTE, ee.getCapabilityVersion());
addCapability(builder);
// Compatibility with old version...
builder = new CapReqBuilder(resource, ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
builder.addAttribute(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, ee.getEEName());
addCapability(builder);
}
public void addAllExecutionEnvironments(EE ee) throws Exception {
addExportPackages(ee.getPackages());
addExecutionEnvironment(ee);
for (EE compatibleEE : ee.getCompatible()) {
addExecutionEnvironment(compatibleEE);
}
}
public void copyCapabilities(Set ignoreNamespaces, Resource r) throws Exception {
for (Capability c : r.getCapabilities(null)) {
if (ignoreNamespaces.contains(c.getNamespace()))
continue;
addCapability(c);
}
}
public void addCapabilities(List capabilities) throws Exception {
if (capabilities == null || capabilities.isEmpty())
return;
for (Capability c : capabilities)
addCapability(c);
}
public void addRequirement(List requirements) throws Exception {
if (requirements == null || requirements.isEmpty())
return;
for (Requirement rq : requirements)
addRequirement(rq);
}
public void addRequirements(List requires) throws Exception {
for (Requirement req : requires) {
addRequirement(req);
}
}
public List findCapabilities(String ns, String filter) throws Exception {
if (filter == null || capabilities.isEmpty())
return Collections.emptyList();
List capabilities = new ArrayList();
Filter f = new Filter(filter);
for (Capability c : getCapabilities()) {
if (ns != null && !ns.equals(c.getNamespace()))
continue;
Map attributes = c.getAttributes();
if (attributes != null) {
if (f.matchMap(attributes))
capabilities.add(c);
}
}
return capabilities;
}
public Map from(Resource bundle) throws Exception {
Map mapping = new HashMap();
addRequirements(bundle.getRequirements(null));
for (Capability c : bundle.getCapabilities(null)) {
CapReqBuilder clone = CapReqBuilder.clone(c);
Capability addedCapability = addCapability0(clone);
mapping.put(c, addedCapability);
}
return mapping;
}
public Reporter getReporter() {
return reporter;
}
public void addContentCapability(URI uri, String sha256, long length, String mime) throws Exception {
assert uri != null;
assert sha256 != null && sha256.length() == 64;
assert length >= 0;
CapabilityBuilder c = new CapabilityBuilder(ContentNamespace.CONTENT_NAMESPACE);
c.addAttribute(ContentNamespace.CONTENT_NAMESPACE, sha256);
c.addAttribute(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri.toString());
c.addAttribute(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, length);
c.addAttribute(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE, mime == null ? "vnd.osgi.bundle" : mime);
addCapability(c);
}
public boolean addFile(File file, URI uri) throws Exception {
if (uri == null)
uri = file.toURI();
Domain manifest = Domain.domain(file);
String mime = "vnd.osgi.bundle";
boolean hasIdentity = false;
if (manifest != null)
hasIdentity = addManifest(manifest);
else
mime = "application/java-archive";
String sha256 = SHA256.digest(file).asHex();
addContentCapability(uri, sha256, file.length(), mime);
return hasIdentity;
}
}