![JAR search and dependency download from the Maven repository](/logo.png)
aQute.launchpad.BundleSpecBuilder Maven / Gradle / Ivy
The newest version!
package aQute.launchpad;
import static aQute.bnd.service.specifications.BuilderSpecification.PROJECT;
import static aQute.bnd.service.specifications.BuilderSpecification.WORKSPACE;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.Version;
import org.osgi.namespace.extender.ExtenderNamespace;
import org.osgi.service.configurator.ConfiguratorConstants;
import aQute.bnd.exceptions.Exceptions;
import aQute.bnd.service.specifications.BuilderSpecification;
import aQute.lib.io.IO;
import aQute.lib.regex.PatternConstants;
import aQute.lib.strings.Strings;
/**
* Provides all builder functions. Specified as default methods so that we can
* create sub builder interfaces that inherit all functions of the outer one.
*/
public interface BundleSpecBuilder {
String CONFIGURATION_JSON = "OSGI-INF/configurator/configuration.json";
Pattern SYMBOLICNAME = Pattern.compile(PatternConstants.SYMBOLICNAME);
BundleBuilder x();
/**
* Bundle-SymbolicName
*/
default BundleSpecBsn bundleSymbolicName(String name) {
BundleBuilder x = x();
if (name == null) {
throw new IllegalArgumentException("bsn cannot be null");
}
if (!SYMBOLICNAME.matcher(name)
.matches()) {
throw new IllegalArgumentException("invalid bsn: " + name);
}
x.spec.bundleSymbolicName.clear();
x.spec.bundleSymbolicName.put(name, new LinkedHashMap<>());
return new BundleSpecBsn() {
@Override
public BundleBuilder x() {
return x;
}
@Override
public String bsn() {
return name;
}
};
}
/**
* The values for a Fragment Attachment directive
*/
enum FragmentAttachment {
always("always"),
never("never"),
resolve_time("resolve-time");
String value;
FragmentAttachment(String name) {
this.value = name;
}
}
/**
* Sub builder for Bundle Symbolic Name directives and attributes
*/
interface BundleSpecBsn extends BundleSpecBuilder {
/**
* Make this bundle a singleton bundle
*/
default BundleSpecBsn singleton() {
directive("singleton", "true");
return this;
}
/**
* Set the bundle's fragment attachment policy
*
* @param attachment the attachment policy
*/
default BundleSpecBsn fragmentAttachment(FragmentAttachment attachment) {
directive("fragment-attachment", attachment.value);
return this;
}
/**
* Set the mandatory matching attributes
*
* @param attributes the matching attributes
*/
default BundleSpecBsn mandatory(String... attributes) {
directive("mandatory", Stream.of(attributes)
.collect(Strings.joining()));
return this;
}
/**
* Bundle-SymbolicName attribute
*
* @param key the attribute name
* @param value the value
*/
default BundleSpecBsn attribute(String key, String value) {
BundleBuilder x = x();
x.add("Bundle-SymbolicName", x.spec.bundleSymbolicName, bsn(), key, value);
return this;
}
/**
* Bundle-SymbolicName directive
*
* @param key the directive name (no ':' at end)
* @param value the value
*/
default BundleSpecBsn directive(String key, String value) {
attribute(key + ":", value);
return this;
}
/*
* the actual bsn
*/
String bsn();
}
/**
* Bundle-Version
*/
default BundleSpecBuilder bundleVersion(String version) {
BundleBuilder x = x();
if (x.spec.bundleVersion != null) {
throw new IllegalArgumentException("Bundle-Version was already set to: " + x.spec.bundleVersion);
}
x.spec.bundleVersion = version;
new Version(version);
return this;
}
/**
* Bundle-Activator
*/
default BundleSpecBuilder bundleActivator(String bundleActivator) {
BundleBuilder x = x();
if (x.spec.bundleActivator != null) {
throw new IllegalArgumentException("Bundle-Activator was already set to: " + x.spec.bundleActivator);
}
x.spec.bundleActivator = bundleActivator;
return this;
}
/**
* Bundle-Activator - Set the Bundle-Activator via a class. This will set
* the Bundle-Activator header and ensures the package of the given class is
* imported from the class path. This allows the caller to use inner classes
* as an activator. The class is also added as a resource so that it could
* potentially also be a DS component. However, this resource is only used
* by bnd since in runtime we will load the class from the classpath.
*
* @param bundleActivator the class that is intended to be the Bundle
* Activator
*/
default BundleSpecBuilder bundleActivator(Class extends BundleActivator> bundleActivator) {
this.bundleActivator(bundleActivator.getName());
this.addResource(bundleActivator);
return this;
}
/**
* Fragment-Host
*/
default BundleSpecFragmentHost fragmentHost(String name) {
BundleBuilder x = x();
x.spec.fragmentHost.put(name, new LinkedHashMap<>());
if (x.spec.fragmentHost.size() > 1)
throw new IllegalArgumentException(
"Only one Fragment-Host can be specified. The previous one was " + x.spec.fragmentHost);
return new BundleSpecFragmentHost() {
/*
* access to the underlying builder
*/
@Override
public BundleBuilder x() {
return x;
}
/*
* access to the current fragmenthost
*/
@Override
public String fragmenthost() {
return name;
}
};
}
/**
* Extension options for the Fragment-Host
*/
enum Extension {
framework,
bootclasspath
}
interface BundleSpecFragmentHost extends BundleSpecBuilder {
/**
* extension
*
* @param extension the actual extension type
*/
default BundleSpecBuilder extension(Extension extension) {
directive("extension", extension.toString());
return this;
}
/**
* The Fragment-Host bundle-version attribute
*
* @param versionRange the range of version for the fragment host
*/
@Override
default BundleSpecFragmentHost bundleVersion(String versionRange) {
attribute("bundle-version", versionRange);
return this;
}
/**
* Fragment-Host attribute
*
* @param key the attribute name
* @param value the attribute value
*/
default BundleSpecFragmentHost attribute(String key, String value) {
BundleBuilder x = x();
x.add("Fragment-Host", x.spec.fragmentHost, fragmenthost(), key, value);
return this;
}
/**
* Fragment-Host directive
*
* @param key the directive name without the ':'
* @param value the directive value
*/
default BundleSpecFragmentHost directive(String key, String value) {
attribute(key + ":", value);
return this;
}
/*
* the currently set fragment host
*/
String fragmenthost();
}
/**
* Require-Bundle
*/
default BundleSpecBuilderRequireBundle requireBundle(String name) {
BundleBuilder x = x();
String actualName = x.prepare(name, x.spec.requireBundle);
return new BundleSpecBuilderRequireBundle() {
/*
* Access to the underlying builder
*/
@Override
public BundleBuilder x() {
return x;
}
/*
* Access to the current requirebundle statement
*/
@Override
public String requirebundle() {
return actualName;
}
};
}
/**
* Require-Bundle sub editor
*/
interface BundleSpecBuilderRequireBundle extends BundleSpecBuilder {
/**
* Require-Bundle reexport directive
*/
default BundleSpecBuilderRequireBundle reepexort() {
directive("visibility", "reexport");
return this;
}
/**
* Require-Bundle optional directive
*/
default BundleSpecBuilderRequireBundle optional() {
directive("resolution", "optional");
return this;
}
/**
* Require-Bundle bundleVersion attribute
*
* @param versionRange the version range of the required bundles
*/
@Override
default BundleSpecBuilderRequireBundle bundleVersion(String versionRange) {
attribute("bundle-version", versionRange);
return this;
}
/**
* Require-Bundle attribute
*
* @param key the name of the attribute
* @param value the value of the attribute
*/
default BundleSpecBuilderRequireBundle attribute(String key, String value) {
BundleBuilder x = x();
x.add("Require-Bundle ", x.spec.requireBundle, requirebundle(), key, value);
return this;
}
/**
* Require-Bundle directive
*
* @param key the name of the directive (without the ':')
* @param value the value of the directive
*/
default BundleSpecBuilderRequireBundle directive(String key, String value) {
attribute(key + ":", value);
return this;
}
/*
* The current require bundle bsn
*/
String requirebundle();
}
/**
* Import-Package
*
* @param packageName name of the package to be imported, may contain
* wildcards
*/
default BundleSpecImportPackage importPackage(String packageName) {
BundleBuilder x = x();
x.spec.importPackage.computeIfAbsent(packageName, k -> new LinkedHashMap<>());
return new BundleSpecImportPackage() {
/*
* Access tot the underlying builder
*/
@Override
public BundleBuilder x() {
return x;
}
/*
* Currently edited package name
*/
@Override
public String packageName() {
return packageName;
}
};
}
/**
* Sub interface for editing an Import-Package addition
*/
interface BundleSpecImportPackage extends BundleSpecBuilder {
/**
* Set the resolution to optional
*/
default BundleSpecImportPackage optional() {
directive("resolution", "optional");
return this;
}
/**
* Set the package version attribute
*
* @param versionRange the version range of the required package
*/
default BundleSpecImportPackage version(String versionRange) {
attribute("version", versionRange);
return this;
}
/**
* Set the import from specific bundle attribute
*
* @param bundleSymbolicName the bundle to import the package from
*/
default BundleSpecImportPackage bundle_symbolic_name(String bundleSymbolicName) {
attribute("bundle-symbolic-name", bundleSymbolicName);
return this;
}
/**
* Set the import from specific bundle version attribute
*
* @param versionRange the version range of the required package
*/
default BundleSpecImportPackage bundle_version(String versionRange) {
attribute("bundle-version", versionRange);
return this;
}
/**
* The Import-Package attribute
*
* @param key the name of the attribute
* @param value the value of the attribute
*/
default BundleSpecImportPackage attribute(String key, String value) {
BundleBuilder x = x();
x.add("Import-Package", x.spec.importPackage, packageName(), key, value);
return this;
}
/**
* The Import-Package directive
*
* @param key the name of the directive (without the ':')
* @param value the value of the directive
*/
default BundleSpecImportPackage directive(String key, String value) {
attribute(key + ":", value);
return this;
}
/*
* Currently edited package name
*/
String packageName();
}
/**
* Export-Package
*/
default BundleSpecExportPackage exportPackage(String name) {
BundleBuilder x = x();
String packageName = x.prepare(name, x.spec.exportPackage);
return new BundleSpecExportPackage() {
/*
* (non-Javadoc)
* @see aQute.launchpad.BundleSpecBuilder#x()
*/
@Override
public BundleBuilder x() {
return x;
}
/*
* the currently exported package name
*/
@Override
public String packageName() {
return packageName;
}
};
}
/**
* The sub interface for editing an exported package
*/
interface BundleSpecExportPackage extends BundleSpecBuilder {
/**
* Set the uses directive
*
* @param packageNames packages used by the current export package
*/
default BundleSpecExportPackage uses(String... packageNames) {
directive("uses", x().join(packageNames));
return this;
}
/**
* Set the mandatory attributes
*
* @param attributeNames the mandatory attribute names
*/
default BundleSpecExportPackage mandatory(String... attributeNames) {
directive("mandatory", x().join(attributeNames));
return this;
}
/**
* Set included classes
*
* @param classNames the included class names
*/
default BundleSpecExportPackage include(String... classNames) {
directive("include", x().join(classNames));
return this;
}
/**
* Set included classes
*
* @param classes the included class names
*/
default BundleSpecExportPackage include(Class>... classes) {
return this.include(Stream.of(classes)
.map(Class::getName)
.toArray(String[]::new));
}
default BundleSpecExportPackage exclude(String... classNames) {
directive("exclude", x().join(classNames));
return this;
}
default BundleSpecExportPackage version(String version) {
attribute("version", version);
return this;
}
default BundleSpecExportPackage attribute(String key, String value) {
BundleBuilder x = x();
x.add("Export-Package", x.spec.exportPackage, packageName(), key, value);
return this;
}
default BundleSpecExportPackage directive(String key, String value) {
attribute(key + ":", value);
return this;
}
String packageName();
}
/**
* -exportcontents
*/
default BundleSpecExportContents exportContent(String name) {
BundleBuilder x = x();
String actualName = x.prepare(name, x.spec.exportContents);
return new BundleSpecExportContents() {
@Override
public BundleBuilder x() {
return x;
}
@Override
public String packageName() {
return actualName;
}
};
}
interface BundleSpecExportContents extends BundleSpecBuilder {
String packageName();
default BundleSpecExportContents uses(String... packageNames) {
directive("uses", x().join(packageNames));
return this;
}
default BundleSpecExportContents mandatory(String... attributeNames) {
directive("mandatory", x().join(attributeNames));
return this;
}
default BundleSpecExportContents include(String... classNames) {
directive("include", x().join(classNames));
return this;
}
default BundleSpecExportContents exclude(String... classNames) {
directive("exclude", x().join(classNames));
return this;
}
default BundleSpecExportContents version(String version) {
attribute("version", version);
return this;
}
default BundleSpecExportContents attribute(String key, String value) {
BundleBuilder x = x();
x.add("-exportcontents", x.spec.exportPackage, packageName(), key, value);
return this;
}
default BundleSpecExportContents directive(String key, String value) {
attribute(key + ":", value);
return this;
}
}
/**
* -privatepackage
*/
default BundleSpecBuilder privatePackage(String name) {
BundleBuilder x = x();
x.prepare(name, x.spec.privatePackage);
return this;
}
/**
* Provide-Capability
*/
default BundleSpecProvideCapability provideCapability(String namespace) {
BundleBuilder x = x();
String actualNamespace = x.prepare(namespace, x.spec.exportPackage);
return new BundleSpecProvideCapability() {
@Override
public BundleBuilder x() {
return x;
}
@Override
public String namespace() {
return actualNamespace;
}
};
}
interface BundleSpecProvideCapability extends BundleSpecBuilder {
String namespace();
default BundleSpecProvideCapability uses(String... packageNames) {
directive("uses", x().join(packageNames));
return this;
}
default BundleSpecProvideCapability effective(String effective) {
directive("effective", effective);
return this;
}
default BundleSpecProvideCapability version(String version) {
attribute("version:Version", version);
return this;
}
default BundleSpecProvideCapability primary(String name) {
directive(namespace(), name);
return this;
}
default BundleSpecProvideCapability attribute(String key, String value) {
BundleBuilder x = x();
x.add("Provide-Capability", x.spec.provideCapability, namespace(), key, value);
return this;
}
default BundleSpecProvideCapability directive(String key, String value) {
attribute(key + ":", value);
return this;
}
}
/**
* Require-Capability
*/
default BundleSpecRequireCapability requireCapability(String namespace) {
BundleBuilder x = x();
String actualNamespace = x.prepare(namespace, x.spec.exportPackage);
return new BundleSpecRequireCapability() {
@Override
public BundleBuilder x() {
return x;
}
@Override
public String namespace() {
return actualNamespace;
}
};
}
interface BundleSpecRequireCapability extends BundleSpecBuilder {
String namespace();
default BundleSpecRequireCapability effective(String effective) {
directive("effective", effective);
return this;
}
default BundleSpecRequireCapability optional() {
directive("resolution", "optional");
return this;
}
default BundleSpecRequireCapability multiple() {
directive("cardinality", "multiple");
return this;
}
default BundleSpecRequireCapability filter(String filter) {
directive("filter", filter);
return this;
}
default BundleSpecRequireCapability attribute(String key, String value) {
BundleBuilder x = x();
x.add("Require-Capability", x.spec.requireCapability, namespace(), key, value);
return this;
}
default BundleSpecRequireCapability directive(String key, String value) {
attribute(key + ":", value);
return this;
}
}
/**
* Include-Resource
*/
default BundleSpecIncludeResource includeResource(String target, String source, boolean preprocess,
boolean absentOk) {
String key = target == null ? source : target + "=" + source;
if (absentOk)
key = "-" + key;
if (preprocess)
key = "{" + key + "}";
return includeResource(key);
}
default BundleSpecIncludeResource includeResource(String spec) {
BundleBuilder x = x();
String actualSpec = x.prepare(spec, x.spec.includeresource);
return new BundleSpecIncludeResource() {
@Override
public BundleBuilder x() {
return x;
}
@Override
public String spec() {
return actualSpec;
}
};
}
default BundleSpecIncludeResource include(String source) {
return includeResource(null, source, false, false);
}
default BundleSpecIncludeResource includeIfPresent(String source) {
return includeResource(null, source, false, true);
}
default BundleSpecIncludeResource preprocessIfPresent(String source) {
return includeResource(null, source, true, true);
}
default BundleSpecIncludeResource preprocess(String source) {
return includeResource(null, source, true, false);
}
default BundleSpecIncludeResource include(String target, String source) {
return includeResource(target, source, false, false);
}
default BundleSpecIncludeResource includeIfPresent(String target, String source) {
return includeResource(target, source, false, true);
}
default BundleSpecIncludeResource preprocessIfPresent(String target, String source) {
return includeResource(null, source, true, true);
}
default BundleSpecIncludeResource preprocess(String target, String source) {
return includeResource(target, source, true, false);
}
interface BundleSpecIncludeResource extends BundleSpecBuilder {
String spec();
default BundleSpecIncludeResource literal(String literal) {
attribute("literal", literal);
return this;
}
default BundleSpecIncludeResource attribute(String key, String value) {
BundleBuilder x = x();
x.add("-includeresource", x.spec.includeresource, spec(), key, value);
return this;
}
default BundleSpecIncludeResource directive(String key, String value) {
attribute(key + ":", value);
return this;
}
}
default BundleSpecBuilder failOk() {
BundleBuilder x = x();
x.spec.failOk = true;
return this;
}
default BundleSpecBuilder sources() {
BundleBuilder x = x();
x.spec.sources = true;
return this;
}
default BundleSpecBuilder instruction(String instruction, String value) {
BundleBuilder x = x();
String put = x.spec.other.put(instruction, value);
return this;
}
default BundleSpecBuilder header(String header, String value) {
BundleBuilder x = x();
x.spec.other.put(header, value);
return this;
}
default BundleSpecBuilder classpath(File... files) {
for (File f : files) {
classpath(f.getAbsolutePath());
}
return this;
}
default BundleSpecBuilder classpath(String path) {
BundleBuilder x = x();
x.spec.classpath.add(path);
return this;
}
/**
* A class add will add the resource of the class to the bundle but it will
* import the class' package from the framework (-classpath). This way DS
* can do its magic since the component class will come from the classpath
* although bnd calculates the XML.
*
* @param class1 the class to add as a resource
*/
default BundleSpecBuilder addResource(Class> class1) {
includeResource(class1.getName()
.replace('.', '/') + ".class").attribute("class", class1.getName());
importPackage(class1.getPackage()
.getName()
.toString());
return this;
}
/**
* Actually add the specified class into the bundle. This contrasts with
* {@link #addResource(Class)}, which imports the specified class via OSGi
* (typically from the system classloader). Classes added using this method
* will be loaded from within the framework, rather than by the system
* framework.
*
* @param class1 the class to add as a resource
*/
default BundleSpecBuilder addResourceWithCopy(Class> class1) {
String className = class1.getName();
String path = className.replace('.', '/') + ".class";
URL url = class1.getClassLoader()
.getResource(path);
if (url == null) {
throw new IllegalArgumentException(
"Couldn't find class file for " + className + ", possibly a synthetic class");
}
addResource(path, url);
return this;
}
default BundleSpecBuilder addResource(String path, URL url) {
try {
File f;
if ("file".equals(url.getProtocol())) {
f = new File(url.toURI());
} else {
f = File.createTempFile("bundlespecbuilder", "conf");
IO.store(url.openStream(), f);
}
x().addClose(() -> IO.delete(f));
includeResource(path, IO.absolutePath(f), false, false);
return this;
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
/**
* Adds a configuration compatible as described by the Configurator spec. It
* will automatically add the necessary Requirement Header.
*
* @param config the {@link File} containing the configuration
* @return the current version of the {@link BundleSpecBuilder}
*/
default BundleSpecBuilder addConfiguration(File config) {
try {
return addConfiguration(config.toURI()
.toURL());
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
/**
* Adds a configuration compatible as described by the Configurator spec. It
* will automatically add the necessary Requirement Header.
*
* @param config the content for the configuration.
* @return the current version of the {@link BundleSpecBuilder}
*/
default BundleSpecBuilder addConfiguration(String config) {
try {
File f = Files.createTempFile("x", "config")
.toFile();
f.deleteOnExit();
IO.store(config, f);
addConfiguration(f);
x().addClose(() -> IO.delete(f));
} catch (Exception e) {
throw Exceptions.duck(e);
}
return this;
}
/**
* Adds a configuration compatible as described by the Configurator spec. It
* will automatically add the necessary Requirement Header.
*
* @param url the URL, where the Configuration can be found
* @return the current version of the {@link BundleSpecBuilder}
*/
default BundleSpecBuilder addConfiguration(URL url) {
return requireCapability(ExtenderNamespace.EXTENDER_NAMESPACE)
.filter(String.format("(%s=%s)", ExtenderNamespace.EXTENDER_NAMESPACE,
ConfiguratorConstants.CONFIGURATOR_EXTENDER_NAME))
.addResource(CONFIGURATION_JSON, url);
}
default Bundle install() throws Exception {
BundleBuilder x = x();
BuilderSpecification spec = x.spec;
byte[] build = x.ws.workspace.build(LaunchpadBuilder.projectDir.getAbsolutePath(), spec);
String location;
if (spec.location == null) {
String name = spec.bundleSymbolicName.toString();
String version = spec.bundleVersion;
version = (version == null) ? "0" : version;
location = name + '-' + version;
} else {
location = spec.location;
}
ByteArrayInputStream bin = new ByteArrayInputStream(build);
return x.ws.getBundleContext()
.installBundle(location, bin);
}
default Bundle start() {
try {
Bundle b = install();
BundleBuilder x = x();
x.ws.start(b);
return b;
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
default BundleSpecBuilder resourceOnly() {
BundleBuilder x = x();
x.spec.resourceOnly = true;
return this;
}
/**
* Inherit the build instructions from the project
*/
@Deprecated
default BundleSpecBuilder inherit() {
BundleBuilder x = x();
x.spec.parent.add(BuilderSpecification.PROJECT);
return this;
}
/**
* Inherit the build instructions from the project
*/
default BundleSpecBuilder project() {
return parent(BuilderSpecification.PROJECT);
}
/**
* Inherit the build instructions from the workspace
*/
default BundleSpecBuilder workspace() {
return parent(BuilderSpecification.WORKSPACE);
}
/**
* Inherit the build instructions from a file. This file must exist. It is
* used as the properties file for the used builder. The further
* specifications from this builder fully overwrite these properties.
*
* @param path
*/
default BundleSpecBuilder parent(String path) {
BundleBuilder x = x();
List paths = x.spec.parent;
String last = paths.isEmpty() ? null : paths.get(paths.size() - 1);
boolean isTerminatedWithProjectOrWorkspace = PROJECT.equals(last) || WORKSPACE.equals(last);
if (isTerminatedWithProjectOrWorkspace)
throw new IllegalArgumentException(
"no entries can be added after either PROJECT or WORKSPACE is set as parent");
boolean isFileParent = !WORKSPACE.equals(path) && !PROJECT.equals(path);
if (isFileParent) {
File f = IO.getFile(x.ws.projectDir, path);
if (!f.isFile())
throw new IllegalArgumentException("No such parent file " + f.getAbsolutePath());
path = f.getAbsolutePath();
}
x.spec.parent.add(path);
return this;
}
/**
* Specify the install location of a bundle. This is the value that is
* passed to {@Link BundleContext#installBundle(String,
* java.io.InputStream)} as the location parameter.
*
* If not specified, the location will default to the string
* bsn-bundleVersion.
*
* @param location the location string to use for this bundle.
* @return The builder object for chained invocations.
*/
default BundleSpecBuilder location(String location) {
BundleBuilder x = x();
x.spec.location = location;
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy