diff --git a/bndtools.core/bndtools.shared.bndrun b/bndtools.core/bndtools.shared.bndrun
index bfe23a4a04..b1ac245963 100644
--- a/bndtools.core/bndtools.shared.bndrun
+++ b/bndtools.core/bndtools.shared.bndrun
@@ -47,6 +47,7 @@
bnd.identity;id='org.bndtools.headless.build.manager',\
bnd.identity;id='org.bndtools.headless.build.plugin.ant',\
bnd.identity;id='org.bndtools.headless.build.plugin.gradle',\
+ bnd.identity;id='org.bndtools.help',\
bnd.identity;id='org.bndtools.launch',\
bnd.identity;id='org.bndtools.remoteinstall',\
bnd.identity;id='org.bndtools.templates.template',\
diff --git a/bndtools.core/bndtools.shared.latest.bndrun b/bndtools.core/bndtools.shared.latest.bndrun
index 43e9c32a34..c62588f34e 100644
--- a/bndtools.core/bndtools.shared.latest.bndrun
+++ b/bndtools.core/bndtools.shared.latest.bndrun
@@ -47,6 +47,7 @@
bnd.identity;id='org.bndtools.headless.build.manager',\
bnd.identity;id='org.bndtools.headless.build.plugin.ant',\
bnd.identity;id='org.bndtools.headless.build.plugin.gradle',\
+ bnd.identity;id='org.bndtools.help',\
bnd.identity;id='org.bndtools.launch',\
bnd.identity;id='org.bndtools.remoteinstall',\
bnd.identity;id='org.bndtools.templates.template',\
diff --git a/org.bndtools.help/.gitignore b/org.bndtools.help/.gitignore
new file mode 100644
index 0000000000..fa76fa8af6
--- /dev/null
+++ b/org.bndtools.help/.gitignore
@@ -0,0 +1,3 @@
+bin/
+bin_test/
+generated/
diff --git a/org.bndtools.help/.project b/org.bndtools.help/.project
new file mode 100644
index 0000000000..9e1f85742d
--- /dev/null
+++ b/org.bndtools.help/.project
@@ -0,0 +1,17 @@
+
+
bnd is the Swiss army knife of OSGi, it is used for creating and working with OSGi bundles. +Its primary goal is take the pain out of developing bundles.
+bnd consists of two major parts
+Its primary function is generating OSGi meta data by analyzing Java classes. There are plugins for popular tools like Eclipse, Maven and Gradle.
+The other part is a IDE/build tool independent model of a workspace with projects.
+With OSGi you are forced to provide additional metadata in the JAR's manifest to verify the consistency of your "class path". This metadata must be closely aligned with the class files in the bundle and the policies that a company has about versioning. Maintaining this metdata is an error prone chore because many aspects are redundant.
+bnd's raison d'etre is therefore to remove the chores and use the redundancy to create the manifest from the class files instead of maintaining it by hand. The core task is therefore to analyze the class files and find any dependencies. These dependencies are then merged with ''instructions'' supplied by the user. For example, adding a version to all imported packages from a specific library can be specified as:
+Import-Package: com.library.*; version = 1.21
+The OSGi manifest must explicitly mention a package, bnd allows the use of wildcards. bnd contains many more such conveniences. bnd roots are about 10 years old and bnd has therefore a large number of functions that remove such chores. These range from simplifying the use of OSGi Declarative Services, working with Spring and Blueprint, WAR and WAB files, version analysis, project dependencies, and much more.
+Over time bnd started to appear in many different incarnations. It is an an ant task, a command line utility, and a bundle for Eclipse. Other projects have used bndlib to create a maven plugin, bndtools and Sigil both Eclipse IDEs, and others. By keeping the core library small and uncoupled (bnd has no external connections except Java 5), it is easy to embed the functionality in other projects.
+Traditionally, JAR files were made with the JDK jar tool, the jar ant task, or the Maven packager. All these tools share the same concept. The developer creates a directory image of the jar by copying files to a directory; this directory is then jarred. This model can be called the ''push'' model. Obviously this method works well.
+bnd works differently, it uses the ''pull'' model. Instructions in the bnd file describe the contents of the desired JAR file without writing this structure to disk. The contents from the output can come from the class path or from anywhere in the file system. For example, the following instruction includes the designated packages in the JAR:
+Private-Package: com.example.*
+bnd can create a JAR from packages the sources, directories or other JAR files. You never have to copy files around, the instructions that Bnd receives are sufficient to retrieve the files from their original location, preprocessing or filtering when required.
+The Jar is constructed from 3 different arguments:
+Export-Package
+Private-Package
+Include-Resource
+Private-Package and Export-Package contain ''instructions''. Instructions are patterns + attributes and directives, looking like normal OSGi attributes and directives. For example:
+Export-Package: com.acme.*;version=1.2
+Each instruction is applied to each package on the classpath in the definition order. That is, if an earlier instruction matches, the later instruction never gets a chance to do its work. If an instruction matches its attributes and properties are applied to the packages. The difference between the Private-Package argument and the Export-Package arguments is that the export version selects the packages for export. If the packages overlap between the two, the export wins.
+An instruction can also be negative when it starts with a '!'. In that case the package is excluded from the selection. For example:
+Export-Package: !com.acme.impl, com.acme.*;version=1.2
+Note that the instructions are applied in order. If the ! instruction was at the end in the previous example, it would not have done its work because the com.acme.* would already have matched.
+The Include-Resource argument can be used to copy resources from the file system in the JAR. This is useful for licenses, images, etc. The instructions in the argument can be a directory, a file, or an inline JAR. The default JAR path is the the root for a directory or the filename for a file. The path can be overridden. Instructions that are enclosed in curly braces, like {license.txt}, are pre-processed, expanding any macros in the file.
+Once the JAR is created, the bnd program analyzes the classes and creates an import list with all the packages that are not contained in the jar but which are referred to. This import list is matched against the Import-Package instructions. Normally, the Import-Package argument is *; all referred packages will be imported. However, sometimes it is necessary to ignore an import or provide attributes on the import statement. For example, make the import optional or discard the import:
+Import-Package: !com.acme.*, *;resolution:=optional
+The arguments to bnd are normal given as a set of properties. Properties that begin with an upper case are copied to the manifest (possibly after processing). Lower case properties are used for macro variables but are not set as headers in the manifest.
+After the JAR is created, the bnd program will verify the result. This will check the resulting manifest in painstaking detail.
+The bnd program works on a higher level then traditional jarring; this might take some getting used to. However, it is much more elegant to think in packages than that it is to think in files. The fact that bnd understand the semantics of a bundle allows it to detect many errors and allows bundles to be created with almost no special information.
+bnd will not create an output file if none of the resources is newer than an existing output file.
+The program is available in several forms: command line, ant task, maven plugin, and an Eclipse plugin.
+ + diff --git a/org.bndtools.help/docs/chapters/120-install.html b/org.bndtools.help/docs/chapters/120-install.html new file mode 100644 index 0000000000..76d79a4943 --- /dev/null +++ b/org.bndtools.help/docs/chapters/120-install.html @@ -0,0 +1,61 @@ + + + + +bnd is not a single product, it is a library (bndlib) used in many different software build environments. It runs inside Maven, ant, gradle, Eclipse, sbt, and maybe one day in Intellij. To install bnd, you will have to install these tools.
+ + +That said, there is also a command line version of bnd, providing an easy way to try out its many features. You can install bnd through brew on MacOS.
+You can also run bnd command as executable jar, which can be downloaded from JFrog:
+# Install bnd CLI
+curl -Lk -o ~/biz.aQute.bnd.jar \
+ https://bndtools.jfrog.io/artifactory/update-latest/biz/aQute/bnd/biz.aQute.bnd/7.2.1/biz.aQute.bnd-7.2.1.jar
+
+# create alias for easy use via 'bnd'
+alias bnd='java -jar ~/biz.aQute.bnd.jar'
+
+# display bnd version to verify installation
+bnd version
+
+# Run commands
+bnd <command>
+
+# Example
+bnd help
+The binaries are available on JFrog:
+If you are a maven user, you can find many versions in central. The coordinates are:
+ <dependency>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>biz.aQute.bndlib</artifactId>
+ <version>7.1.0</version>
+ </dependency>
+bnd is maintained at github. If you want to change the code, just clone it and modify it. In general we accept pull requests, and often even highly appreciate them.
+The manual is also on github. If you see an improvement, do not hesitate to clone the repo and create a pull request. Improvements are bug corrections but we also like short articles about how to do do something with bnd.
+If you're behind a firewall that requires proxies or you use repositories that require authentication see -connection-settings.
+ + diff --git a/org.bndtools.help/docs/chapters/123-tour-workspace.html b/org.bndtools.help/docs/chapters/123-tour-workspace.html new file mode 100644 index 0000000000..6bc04f1c40 --- /dev/null +++ b/org.bndtools.help/docs/chapters/123-tour-workspace.html @@ -0,0 +1,132 @@ + + + + +The bnd workspace is a foundational concept in bnd, providing the structure and context in which OSGi projects are developed and built.
+In the introduction we mentioned this as the second part of bnd:
+++The other part is a IDE/build tool independent model of a workspace with projects.
+
This guide provides a high-level overview of how to set up and use bndlib workspaces and projects. It is aimed at build engineers or developers responsible for configuring OSGi builds using bnd, and it focuses on the underlying concepts and mechanics that bndlib offers for automating build tasks.
+Rather than teaching OSGi itself or prescribing a full build system, this guide demonstrates how bndlib operates under the hood—tool-agnostic and from the command line—using a sample workspace named com.acme.prime. While the bnd command-line interface is used for illustration, this is not the typical way bndlib is consumed in practice. Most real-world builds are orchestrated through tools like Gradle or IDEs like Bndtools.
+Think of this guide as a conceptual tour that will help you understand how workspaces, properties, plugins, and projects come together in bndlib. The goal is to clarify the structure and extensibility of a bndlib-powered build environment, not to provide production-ready build instructions.
+Since this section provides a tool independent view of bndlib, we use the bnd command line application to demonstrate the features. Though this is an excellent way to show the low level functionality (the porcelain in git terms), it is not the normal way bndlib is used. In general, a build tool like gradle, ant, make, or maven drives this process from the command line an IDE like bndtools handles the user interaction. So please, please, do not take this com.acme.prime as a guide how to create a build. However handy bnd is, it falls far short of a real build tool like for example gradle.
A workspace is a directory with the following mandatory files/directories:
+./
+ cnf/
+ build.bnd
+ ext/
+That's all! So let's create one:
+$ bnd add workspace com.acme.prime
+$ cd com.acme.prime
+$ ls
+cnf
+The cnf directory is the 'magic' directory that makes a directory a workspace, just like the .git directory does for git.
In the cnf directory you find the following files:
$ ls cnf
+bnd.bnd build.bnd ext
+The build.bnd is part of the magic to make something a workspace, it contains your workspace properties. The ext directory contains more properties. The bnd.bnd is the last piece of magic, it makes bndlib recognize the cnf directory as a project.
Why such a long name for the workspace? Wouldn't just tour suffice? Well, glad you asked. If you work with bnd(tools) for some time you will find that you will get many different workspaces since a workspace is another level of modularity. You can see a workspace as a cohesive set of bundles; just like any module it can import and export. Just like any other module, it imports and exports the thing it encapsulates. For example, a bundle imports and exports packages. In case of the workspace this is the bundle. A workspace imports and exports bundles through repositories.
A good module has cohesion; this means that its constituents have a rather strong relation. Since they also tend to come from the same organization they will have very similar bundle symbolic names. Since some of these bundles will escape in the wild it is always beneficial if you can quickly find the source of that bundle. Therefore naming the workspace with the shared prefix of the bundle symbolic names of its constituents is highly recommended.
+That said, bndlib does not enforce this guideline in any way, unlike project names. You can name your workspace foo if you want to.
A workspace so created is quite empty. However, if we look in the cnf directory then we can see the build.bnd file. This file is 100% for you ... Any property, instruction, or header specified in here is available in anything you build in this workspace; all other bnd files will inherit everything from this properties file. In this file should add for example the headers Bundle-Vendor and Bundle-Copyright. However, using the macro language we can also add custom variables and macros that are useful across the workspace.
An important aspect of the workspace is that it hosts plugins. A plugin is an extension to bndlib that gets loaded when the workspace is opened. Plugins provide a lot of different functions in bndlib. You can see the currently loaded plugins with bnd:
+$ bnd plugins
+000 Workspace [com.acme.prime]
+001 java.util.concurrent.ThreadPoolExecutor@a4102b8[Running, ...]
+002 java.util.Random@11dc3715
+003 Maven [m2=...]
+004 Settings[/Users/aqute/.bnd/settings.json]
+005 bnd-cache
+006 ResourceRepositoryImpl [... ]
+007 aQute.bnd.osgi.Processor
+The plugins you see are the built-in plugins of bnd itself, they always are available. However, the purpose of plugins is to extend the base functionality. As almost everything, the set of external plugins is managed through an instruction, which is a property in the Workspace.
+Before bndlib reads the build.bnd file to read the workspace properties, it first reads all the files with a .bnd extension in the cnf/ext folder. The purpose of this folder is to manage setups for plugins. We can add additional plugins by their name. You can see a list of built-in plugins with the add plugin command:
$ bnd add plugin
+Type Description
+ant aQute.bnd.plugin.ant.AntPlugin
+blueprint aQute.lib.spring.SpringXMLType
+eclipse aQute.bnd.plugin.eclipse.EclipsePlugin
+filerepo aQute.lib.deployer.FileRepo
+git aQute.bnd.plugin.git.GitPlugin
+gradle aQute.bnd.plugin.gradle.GradlePlugin
+...
+An interesting plugin is the Eclipse plugin that will prepare any projects for Eclipse. We could also add the git plugin that will make sure the proper .gitignore files are in place.
+$ bnd add plugin eclipse
+$ bnd add plugin git
+$ bnd plugins
+...
+007 aQute.bnd.osgi.Processor
+008 EclipsePlugin
+009 GitPlugin
+Since you likely need to maintain this build it is good to know how this is stored. If you look in the cnf/ext directory you should see an eclipse.bnd and a git.bnd file:
$ ls cnf/ext
+eclipse.bnd git.bnd
+The eclipse.bnd file contains the following:
$ more cnf/ext/eclipse.bnd
+#
+# Plugin eclipse setup
+#
+-plugin.eclipse = aQute.bnd.plugin.eclipse.EclipsePlugin
+So how does this work? When the workspace is opened bndlib will first read all the bnd files in the cnf/ext directory in alphabetical order. After that, it will read the build.bnd file. The idea of the cnf/ext files is that they should not be touched by you, the build.bnd file is, however, all yours. You can override any previous setting in the build.bnd file since it is read last.
As you can see, the eclipse.bnd file defines the property -plugin.eclipse. In most cases that a value should be settable in different places, bndlib uses merged properties. When bndlib loads the plugins, it actually gets the property -plugin, merged with any other property that has a key that starts with -plugin. (ordered alphabetically). This allows you to add to a merged property anywhere in the many places in bndlib where you can set properties.
So now we can try to build the workspace:
+$ bnd build
+No Projects
+Which makes some sense ...
+An empty workspace is not so useful, let's add a project.
+$ bnd add project com.acme.prime.hello
+$ ls -a com.acme.prime.hello/
+. .gitignore bin_test src
+.. .project bnd.bnd test
+.classpath bin generated
+This classic layout defines separate source folders for the main code and the test code. The generated directory is a temporary directory, it contains the artifacts produced by this build.
This is a classical Eclipse layout, with a separate src and test folder. However, this is not baked into bndlib, it is possible to, for example, use the maven layout with the src/main/java, src/test/java, and target directories. We can try this out with the maven plugin.
$ bnd add plugin maven
+$ more cnf/ext/maven.bnd
+-plugin.maven = aQute.bnd.plugin.maven.MavenPlugin
+
+-outputmask = ${@bsn}-${versionmask;===S;${@version}}.jar
+
+src=src/main/java
+bin=target/classes
+testsrc=src/test/java
+testbin=target/test-classes
+target-dir=target
+The maven plugin adds a number of properties that are recognized by bndlib and used appropriately.
+ + diff --git a/org.bndtools.help/docs/chapters/125-tour-features.html b/org.bndtools.help/docs/chapters/125-tour-features.html new file mode 100644 index 0000000000..c97c44bef2 --- /dev/null +++ b/org.bndtools.help/docs/chapters/125-tour-features.html @@ -0,0 +1,408 @@ + + + + +This guide is about the first part of bnd mentioned in the introduction:
+++Its primary function is generating OSGi meta data by analyzing Java classes.
+
While most developers interact with OSGi through user-friendly tools like bndtools or Gradle plugins, the real power of bnd lies in its underlying library: bndlib. This library is the engine that automates the generation of OSGi metadata, wraps third-party JARs, and provides powerful build-time analysis and transformation of bundles.
+This guide is intended for build engineers and advanced users who need precise control over bundle construction—especially when dealing with legacy or non-OSGi JARs. If you’re new to OSGi or just getting started, we recommend beginning with a higher-level tutorial or IDE-based tooling. bndlib relates to OSGi like the ASM byte code manipulation library relates to Java. Sometimes incredibly useful but in general something you do not want to touch, and obviously not the way to learn Java.
+Here, we’ll explore core bndlib features using the command-line bnd tool to manipulate and inspect JARs, understand manifests, control import/export behavior, manage versions, and use macros to keep your builds clean and DRY. While bnd can be a sharp instrument, it becomes invaluable when conventional tools fall short.
+First a word of warning: the fact that bndlib provides a function is in no way an advertisement to use that function. bndlib grew up together with the OSGi specifications and has been used to build the Reference Implementations (RI) and Test Compatibility Kits (TCK). Though this has a lot of benefits for you, the disadvantage is that it also has to support all the bad parts of the specifications, and even sometimes must be able to create erroneous situations so we could create test cases. And we also had to handle the situations caused by the mess of non-modular bundles out there.
+So to make it crystal clear: the fact that a function is in bndlib does not mean it is intended to be used. This section contains a whole bunch of things you wish you never had to touch, and if you do OSGi properly, you will only see a tiny fraction of bndlib. That said, when the unprepared JARs hit the OSGi framework, it is nice to have bndlib as backup.
+This tour uses the bnd command line bnd to demonstrate much of the inner details of bndlib.
+bnd became the Swiss army knife to manipulate JARs and gained a lot of function you should never attempt to use. However, this chapter will give you an overview of what is in bnd.
+bnd has lots of commands to try out aspects of bndlib. The install section shows how to install bnd.
+A common use of bnd is to wrap a JAR file; there are still too many files out there that have no OSGi metadata.This lack of metadata makes them kind of useless as a bundle since it will be fully isolated from the other bundles. In OSGi, you must explicitly indicate what packages should be imported and what packages should be exported, by default everything is private. Though this can be quite frustrating sometimes, a rather strong argument can be made for the long term benefits of this strategy.
+The OSGi metadata is stored in the manifest of the JAR file. The manifest is a like a properties file stored in META-INF/MANIFEST.MF in the JAR file. The manifest format is specified by Sun/Oracle. Though the manifest is a text file and human readable it has a number of quirks that makes it hard to edit for humans. So even though it is text, the intention of the manifest was never to be done by hand, bnd like tools were always the intention. Tools can provide many useful defaults, handle expansions, and verify consistency,
So let's use bnd to wrap the javax.activation.jar file. Let's download it:
+$ mkdir jar
+$ bnd copy https://repo.maven.apache.org/maven2/javax/activation/activation/1.1.1/activation-1.1.1.jar \
+ jar/javax.activation-1.1.1.jar
+bnd provides a convenience function to print out the contents of a JAR. The --uses option shows the packages and what other packages they use.
+$ bnd print --uses jar/javax.activation-1.1.1.jar
+[USES]
+com.sun.activation.registries []
+com.sun.activation.viewers [javax.activation]
+javax.activation [com.sun.activation.registries]
+In the simplistic case our goal is to provide a manifest that makes all the packages in the source JAR available to the other bundles (exports) and that imports anything the classes in the JAR require from the external world (imports). bnd is controlled by a bnd file. This file is a standard Java properties file. In this properties file we can provide headers, instructions, and macros to bnd. In general, a header starts with an upper case, and an instruction with a minus sign ('-'). The format of the headers and instructions is defined by the OSGi specifications. The headers specifically try to follow the structure of an OSGi header. Since exports are defined with the Export-Package header and imports are defined by the Import-Package header we can create a bnd file that looks very much like the desired manifest, except for the instruction that defines our class path. We could also use the URL directly on the classpath but this would download the JAR everytime we ran bnd. So javax.activation.bnd should look like:
-classpath: jar/javax.activation-1.1.1.jar
+Export-Package: *
+The casing of the headers is important, even though OSGi headers are case insensitive. That is Export-Package is not the same for bnd as Export-package.
+The properties format we use in bnd is darned flexibile. A header is recognized when you start the line with a header name (a word without spaces) and then:
+Though lines can be as long as you want, it is better for your brains to keep them short. You can break a line with a backslash ('\') followed by a newline ('\n'). Be careful, the newline must follow the backslash or you will get whatever comes after the backslash in the header. Some examples that all set the variable foo to 3:
foo = 3
+foo:3
+foo 3
+foo \
+3
+Since bnd files are property files, you cannot repeat a property. Later properties will completely overwrite earlier ones and there is no order between properties.
+Notes on backslashes in .bnd files: Be aware that Java properties parsing silently drops single backslashes (or more precisely odd numbers of consecutive backslashes). In the past, this was a common source of confusion, but since bnd 7.2.0, a warning is issued when such cases are detected.
+For example consider this .bnd file:
a: 42
+a0: ${a}
+a1: \${a}
+a2: \\${a}
+This produces the following output:
+a: 42
+a0: 42
+a1: 42
+a2: ${a}
+Notice that a0 and a1 resolve to the same value because the single backslash in a1 is silently dropped by Java’s property parser.
We can now wrap the source JAR with the following command:
+$ bnd javax.activation.bnd
+bnd has many commands, you can find out more about them with thebnd help [sub] command. In this case we rely on bnd detecting a bnd file and doing the right thing. bnd is also prepared to show the manifest of any bundle, so let's take a look at the bundle we just generated:
$ bnd javax.activation.jar
+[MANIFEST javax.activation]
+Bnd-LastModified 1407229057278
+Bundle-ManifestVersion 2
+Bundle-Name javax.activation
+Bundle-SymbolicName javax.activation
+Bundle-Version 0
+Created-By 1.8.0 (Oracle Corporation)
+Export-Package com.sun.activation.registries;version="0.0.0",
+ com.sun.activation.viewers;uses:="javax.activation";version="0.0.0",
+ javax.activation;version="0.0.0"
+Manifest-Version 1.0
+Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.4))"
+Tool Bnd-2.4.0.201408050759
+When we create the JAR, we can also specify the output file. By default, bnd will store JAR under the same name as the bnd file, but then with a .jar extension. You can override the default from the command line:
$ mkdir bundle
+$ bnd do --output bundle javax.activation.bnd
+$ ls bundle
+javax.activation.jar
+You can either specify a directory or a file name. However, you can also specify the output in the bnd file itself:
+-output: bundle
+It is clear that bnd has been busy. It detected 3 packages (com.sun.activation.registries, com.sun.activation.viewers, and javax.activation) and since we exported all, they appear in the manifest as exports. Observant readers will have noticed that we used wildcards ('*'). The OSGi specifications are not so generous, every package must be explicitly listed so that the manifest contains all the information required to process a bundle. This, again, has many long term macro advantages. It's however bnd's goal in life to provide the convenience in this model and that is the reason that bnd supports wildcards in virtual any situation where they are usable. A wildcard requires a domain to be effective, you can only select something out of a set, its domain. In the case of Export-Package, its domain is the class path. This class path is set by the -classpath instruction. In this case the activation jar.
Currently our package is exported at version 0.0.0. This is not a very useful version, we therefore need to version the exported packages. Why? Well, this is the contract of modularity. To get the benefits of privacy (the privilege to change whenever we feel the need) comes the cost of compatibility for the parts we share. If you export a package, you intend it to be used by others. These others are an uncontrollable bunch that will use your revision (the actual artifact with a given digest, i.e. the instance of the program) but will expect updates in the future with new functionality and bug fixes.
+Since a software program is an ephemeral product that changes over time these others do not become dependent on the revision, nope, they become dependent on the program. This creates two problems.
+The answer to these two questions is given to us by semantic versioning, a prescriptive syntax for versions. Though semantic versions work for bundles, they really shine for packages. The basic idea is that when you compile against a version, this version is recorded in the OSGi metadata as an range on the imported package. The floor of this range is the version in the revision use to compile against, the ceiling indicates the first version that would not be compatible anymore. For example, [1.2,2) indicates that any version that is larger or equal than 1.2 and less than 2 is compatible. Therefore, 1.5 would be compatible. Since OSGi supports version ranges on the Import-Package header it will be able to control what revisions get linked and how.
Such a version scheme of course only works if you agree how to use the version syntax, this should explain the 'semantic' in semantic versioning. Therefore, the bnd/OSGi versions have a strictly defined syntax:
+Long story, but it is at the heart of what we're trying to achieve: evolving large systems in a anti-fragile way.
+Back to the exported packages. We can add the version to the export-package by decorating the Export-Package header:
+-classpath: jar/javax.activation-1.1.1.jar
+Export-Package: *;version=1.1.1
+Let's take another look:
+$ bnd javax.activation.bnd
+$ bnd javax.activation.jar
+[MANIFEST javax.activation]
+...
+Export-Package com.sun.activation.registries;version="1.1.1",
+ com.sun.activation.viewers;uses:="javax.activation";version="1.1.1",
+ javax.activation;version="1.1.1"
+...
+Looking at the manifest we can see the uses directive; this directive is the primary reason you should use bnd. To prevent wiring the wrong bundles together, the OSGi Framework must know which packages use what other packages in their API, this list is reflected in the uses constraints. It allows the OSGi Framework to handle multiple versions of the same package in one VM and not create Class Cast Exceptions ... Very powerful but it is impossible to maintain this list by hand and it would be prohibitively expensive in runtime.
+An exported package is a commitment to any future users out there. This commitment will restrict your freedom to make changes since users can get pretty nasty if you make something that will not work with their existing software. In contrast, a package that is not exported is private; since no external user can have knowledge about this package we have complete freedom to do whatever we want in a future revision without getting hollering users on the phone.
+In our example javax.activation JAR we should therefore not export the com.sun.* packages since they are actually private. The Private-Package header has the same syntax as the Export-Package header but will not export the packages, it will keep them hidden in the bundle. Unlike the Export-Package header, it is not necessary to decorate the packages with a version since nobody can depend on them.
-classpath: jar/javax.activation-1.1.1.jar
+Export-Package: javax.activation;version=1.1.1
+Private-Package: com.sun.activation.*
+
+$ bnd javax.activation.bnd
+$ bnd javax.activation.jar
+[MANIFEST javax.activation]
+...
+Export-Package javax.activation;version="1.1.1"
+Private-Package com.sun.activation.registries,
+ com.sun.activation.viewers
+...
+The Private-Package has no meaning to OSGi, if you want to not have this header in your manifest then you can also use -private-package instead.
The Bundle-Version header obviously provides the version. Default it will use version 0, so we should override it. We could just add the 1.1.1 version from our source JAR, however, this would make it impossible to to distinguish the different variations we might make over time. A good tool is to use the ${tstamp} macro for the qualifier. The qualifier is the last part of the version. A macro provides a textual replacement, bnd has hundreds of macros (including access to Javascript) and you can define your own. The ${tstamp} macro provides a time stamp.
-classpath: jar/javax.activation-1.1.1.jar
+Export-Package: javax.activation;version=1.1.1
+Private-Package: com.sun.activation.*
+Bundle-Version: 1.1.1.${tstamp}
+The 'Do Not Repeat Yourself' mantra encodes one of the more important lessons of software engineering. You should always strive to define a 'thing' or a piece of knowledge in only one place. In our little example, we actually repeat ourselves with the version 1.1.1:
+bnd has a macro processor on board that is a life saver if you are addicted to DNRY (we are). This macro processor has access to all properties in the bnd file, including headers and instructions. Therefore ${Bundle-Version} refers to whatever value the Bundle Version is set to. However, in this case the Bundle Version also contains the time stamp in the qualifier, which we do not need in the other places. That is, we do not want the output file name to contain the qualifier.
bnd also contains a large number of built-in macros that provide common utilities. The ${versionmask;mask;version} is for example such a utility for picking out the parts of a version, bumping it, as well as normalizing it. Normalizing (making sure all parts are present, leading zeros removed, etc.) is crucial to keep things workable.
We can try out the macro from the command line, the bnd command has a macro sub command:
$ bnd macro "version;===;1.2.3.q" => 1.2.3
+$ bnd macro "version;+00;1.2.3.q" => 2.0.0
+$ bnd macro "version;+00;0" => 1.0.0
+$ bnd macro "version;=;1.2.3.q" => 1
+To reuse the Bundle Version in the class path, the output, and the export package:
+-classpath: jar/javax.activation-${versionmask;===;${Bundle-Version}}.jar
+
+Export-Package: javax.activation;version=${versionmask;===;${Bundle-Version}}
+Private-Package: com.sun.activation.*
+Bundle-Version: 1.1.1.${tstamp}
+This of course looks awkward and hardly DNRY. A better solution is to create an intermediate variable. Variables are always lower case (if they started with an upper case, they would end up in the manifest). Variables can be referred to with the macro syntax of ${}.
v: 1.1.1
+
+-classpath: jar/javax.activation-${v}.jar
+-output: bundle
+
+Bundle-Version: ${v}.${tstamp}
+Export-Package: javax.activation;version=${v}
+Private-Package: com.sun.activation.*
+Maven Central is quickly moving towards a million revisions organized in 200.000 programs. It should be clear that we need to organize this huge pile of software. One way is to make sure you document your programs appropriately. It is highly recommended to have a short one paragraph description in each bundle. Adding a description to our bundle is easy:
+v: 1.1.1
+
+-classpath: jar/javax.activation-${v}.jar
+-output: bundle
+
+Bundle-Description: \
+ A wrapped version of javax.activation ${v} JAR from Oracle. \
+ This bundle is downloaded from maven and wrapped by bnd.
+Bundle-Version: ${v}.${tstamp}
+Export-Package: javax.activation;version=${v}
+Private-Package: com.sun.activation.*
+For longer descriptions you can continue on the next line with a backslash ('\') followed by a newline. Unlike the manifest, it does not has to start with a space though indenting the text for the next lines is usually a good practice. You cannot use newlines or other markup in the Bundle-Description.
+That said, a good Bundle-Description is a single paragraph with a few short sentences.
+The Bundle-Description is an excellent way to provide a short description, but what about a longer description with examples of usage, caveats, configuration requirements, etc? It would be nice to include a readme file in the bundle that indexing sites could use. You can achieve this effect by including a readme.md file in the root of your bundle.
In bnd it is possible to include any resource from anywhere in the file system (actually, any URL as well).
+-includeresource: readme.md
+The resource is of course not always in the proper place, it is therefore possible to make the output name different from the file path:
+-includeresource: readme.md=doc/readme.md
+In this case, the readme could benefit from the bnd macro support. For example, it could then contain the actual version of the artifact or use any of the other macros available. Preprocessing can be indicated by enclosing the clause with curly braces ('{' and '}').
+-includeresource: {readme.md=doc/readme.md}
+Another option is to encode the text for the readme in the bnd file using the literal option (though this is not such a good idea for any decent readme file since it is hard to use newlines):
+-includeresource: readme.md;literal=${unescape;#JAF\nThis is the Java Activation Framework}
+The -includeresource instruction is quite powerful, there are many more options to recurse directories, filter, etc. See -includeresource.
So far, we've ignored the imported packages because the javax.activation JAR only depends on java.; java. packages are not imported, the OSGi Framework will always provide access to them. Let's add another JAR, the javax.mail jar that uses javax.activation. This JAR is already a bundle, which is really good. Except for this exercise, so we copy and strip the OSGi metadata
+$ bnd copy --strip https://repo.maven.apache.org/maven2/com/sun/mail/javax.mail/1.5.2/javax.mail-1.5.2.jar \
+ > jar/javax.mail-1.5.2.jar
+$ bnd print --uses jar/javax.mail-1.5.2.jar
+[USES]
+com.sun.mail.auth [javax.crypto, ... ]
+com.sun.mail.handlers [javax.activation, ... ]
+com.sun.mail.iap [com.sun.mail.util, ... ]
+com.sun.mail.imap [com.sun.mail.imap.protocol, ...]
+com.sun.mail.imap.protocol [com.sun.mail.iap, ... ]
+com.sun.mail.pop3 [javax.mail, ... ]
+com.sun.mail.smtp [com.sun.mail.util, ... ]
+com.sun.mail.util [javax.mail, ... ]
+com.sun.mail.util.logging [javax.mail, ...]
+javax.mail [javax.mail.event, ... ]
+javax.mail.event [javax.mail]
+javax.mail.internet [javax.mail, ... ]
+javax.mail.search [javax.mail.internet, javax.mail]
+javax.mail.util [javax.mail.internet, javax.activation]
+The javax.mail bundle leverages the Java Activation Framework (JAF) embedded in our previous javax.activation bundle. One of the most useful features in bnd is that it can analyze the class files to find the package dependencies and turn them into an Import-Package header. If bnd finds the exported packages with a version on the class path then it will automatically use the exported versions to calculate the import version range. That is, if the package is exported as 1.1.1, then bnd will import it in general as [1.1,2).
So lets wrap the javax.mail package, add to javax.mail.bnd:
v: 1.5.2
+
+-classpath: \
+ jar/javax.activation-1.1.1.jar, \
+ jar/javax.mail-${v}.jar
+
+Bundle-Version: ${v}
+Bundle-Description: \
+ An OSGi wrapped version of the javax.mail library downloaded from maven.
+Export-Package: javax.mail.*
+Private-Package: com.sun.mail.*
+bnd contains a special -i/--impexp option to print the imports and exports of a bundle. So lets make the bundle and see:
$ bnd javax.mail.bnd
+$ bnd print -i javax.mail.jar
+[IMPEXP]
+Import-Package
+ javax.activation
+ javax.crypto
+ javax.crypto.spec
+ javax.mail.event {version=[1.5,2)}
+ javax.mail.search {version=[1.5,2)}
+ javax.mail.util {version=[1.5,2)}
+ javax.net
+ javax.net.ssl
+ javax.security.auth.callback
+ javax.security.auth.x500
+ javax.security.sasl
+ javax.xml.transform
+ javax.xml.transform.stream
+Export-Package
+ javax.mail {version=1.5}
+ javax.mail.event {version=1.5, imported-as=[1.5,2)}
+ javax.mail.internet {version=1.5}
+ javax.mail.search {version=1.5, imported-as=[1.5,2)}
+ javax.mail.util {version=1.5, imported-as=[1.5,2)}
+First, we notice that there are quite a few imports that have no import range. This is not good but unfortunately Java's VM package versions are more or less absent and are clearly not semantically versioned. A correctly setup system ensures that the correct execution environment is used.
+However, we also have in this list javax.activation, for this package we do have an export, the problem is that we included the original JAR on the class path and not the bundle we generated.
+v: 1.5.2
+
+-classpath: \
+ bundle/javax.activation.jar, \
+ jar/javax.mail-${v}.jar
+
+Bundle-Version: ${v}
+Bundle-Description: \
+ An OSGi wrapped version of the javax.mail library downloaded from maven.
+Export-Package: javax.mail.*
+Private-Package: com.sun.mail.*
+And now build + print:
+$ bnd javax.mail.bnd; bnd print -i javax.mail.jar
+[IMPEXP]
+Import-Package
+ javax.activation {version=[1.1,2)}
+ javax.crypto
+ ...
+Yes! Now the javax.activation package is imported with a range of [1.1,2). You now may wonder why not [1.1.1,2)? Well, the reason is that changes in the micro part of the version should not make a difference in API. If you include the micro part in the import range then it turns out that the overall system becomes very volatile, small changes become large quickly. Ignoring the micro part in the import range is like a bit of oil in the engine ... However, if you feel uncomfortable with this lubricant then it is possible to override this.
We got the Import-Package header in the manifest but we never specified it. The reason is that bnd has a default Import-Package set to '*'. There is a very good reason for this, it works almost exactly as it should work. Any time you feel the desire to muck around with the imports you should realize that you're not doing the right thing. That said, it is the unfortunate truth that sometimes doing the least worst thing is the best option ...
+Assume we want to provide a version range on some of the imported packages because these packages could also be provided by bundles, they do not have to come from the JVM. For this, we can add the following header that will decorate the javax.net.ssl package:
+Import-Package: javax.net.*;version=1.1, *
+You can decorate any package, including packages specified with a wildcard. The domain of the Import-Package is all packaged that are referred to by any class inside the bundle. If there are for example imports
+The removeheaders header uses a selector expression. A selector expression consists of a number of comma separated clauses. A clause contains a name (which can be wildcarded) and some optional instructions. A selector is always applied to a domain, in the case of -removeheaders the domain is all the headers in the manifest. The clauses in the selector are applied one by one in their declaration order to the domain. If a clause matches a member of the domain then the member is removed from the set, and the next member is again matched against the first clause. A suffix of ':i' indicates a case insensitive match.
This ordering is important because selector clauses can use negation. When a clause starts with an exclamation mark ('!') then a matching member is further ignored. Therefore, the following selector will never match the Bundle-Name, the first clause already threw any header that starts with Bundle- out:
-removeheaders: !Bundle-*, Bundle-Name:i
+The Require-Capability header ensures that the runtime has at least a Java 1.4 VM by requiring an OSGi execution environment. bnd calculates this requirement based on the compiler version used to compile the classes in the JAR. We can override this requirement or disable it with the -noee=true instruction.
We now have 2 files: javax.activation.bnd and javax.mail.bnd. If we would like to add some more descriptive headers like Bundle-Vendor, Bundle-DocURL, and Bundle-Copyright then we would have to add them to both files, another case of DNRY. Since this is quite common, we can use includes.
+-include: common.bnd
+In common.bnd we can then place:
+Bundle-Vendor: Oracle (wrapped by bnd)
+Bundle-Copyright: (c) Oracle, All Rights Reserved
+These headers are then automatically placed in the manifests of our target bundles.
+A common mistake with the -include instruction is that it is repeated several times. Since bnd files are property files, only one of them survives. However, the -include instruction accepts multiple files/URLs to both property files or manifest files. You can also indicate that it is ok to not have a certain file by prefixing it with a minus sign ('-').
-include common.bnd, -other.bnd
+Always use Unix like paths, using the forward slash ('/') as file separator, also on Windows. It is highly recommended to always use relative paths since any absolute references makes your builds unportable.
+In our progression towards wrapping javax.activation and javax.mail we have created an unmanaged ordering; we can only build javax.mail once the javax.activation is created. For this reason, bnd can also create a control file that steers sub bnd files. The control file can also contain common properties, though these can be overridden by the sub files. Place the following content in javax.bnd:
+Bundle-Vendor: Oracle (wrapped by bnd)
+Bundle-Copyright: (c) Oracle, All Rights Reserved
+-output: bundle
+-sub: \
+ javax.activation.bnd, \
+ javax.mail.bnd
+We can now build the jars with a simple command:
+$ rm bundle/*
+$ bnd javax.bnd
+$ ls bundle
+javax.activation.jar javax.mail.jar
+The basic model of bnd is to collect packages from the classpath and assemble them in a bundle. This pull model is quite different from the more common push model in builds where it is harder to include packages from other projects. However, bnd's model makes it quite easy to create a bundle out of multiple JARs. So lets add a new bnd file that merges javax activation and mail.
+Actually, javax.activation and javax.mail are really bad citizens in an OSGi world. They use class loading hacks all over the place that make a mockery out of modularity. Part of the problem is that they really require certain private directories to be available from the client's class loader. In those cases it is sometimes necessary to make sure the layout of the bundles is exactly the same as the source bundles.
+The best way to achieve this is to unroll the source bundles in the target bundle. You can unroll a JAR by prefixing it with a commercial at sign ('@') in an include resource operation. Lets get started on a javax.mail.all.bnd file:
+-includeresource: \
+ @jar/javax.activation.jar, \
+ @jar/javax.mail.jar
+This instruction combines the two JARs into one.
+There are some common pitfalls that can be prevented by following the tips:
+One of the more surprising things we've learned about modularity in the last decade is how much of a fractal pattern it actually is. All the way from CPU machine instructions all the way up to a large distributed system you can see the pattern of encapsulation to keep as much details as possible confined to the internals of the module. Strangely enough, as an industry we seem to have a hard time learning the lessons from the lower layers where we over time learned the rules to the layers above.
+Java was developed the early 90's as a state of the art language based on Object Oriented concepts. It did apply the rules of modularity on the method and class level and pioneered with providing a higher level module with their concept of packages. However, our software industry did their utmost to ignore this concept, helped by the fact that it did have some real shortcomings.
+The key shortcoming was that it was hard to keep classes private since they required public visibility when they crossed the package boundary. This collided with the granularity of the package, in general you needed to break an application in many different packages to stay sane as a developer; with the consequence that a lot of private code was forced to become public.
+Java did provide a higher level module: the JAR. However, this was a concept outside the language, it was only visible in runtime to be used by the class loaders. Interestingly, it was then used in runtime security but it was kept outside the language that still desperately tried to front a single name space. Problems showed up as class cast exceptions, package private access violations in the same package, and getting the wrong class. It is amazing how brittle the class path model of Java really is.
+In search for a model that would allow the management of hundreds of thousands of devices running Java the OSGi decided to leverage the JAR but turn it in to a proper module by adding encapsulation. Since in modularity a module tends to export/import entities that have a lower granularity (classes export/import methods, packages import/export classes), the OSGi solution was to export/import packages.
+Package imports/exports was a much maligned choice because the maligners did not understand that some of the lessons learned from object oriented programming, where the class is the module, also applied to the other kinds of modules. The greatest problem with Object Oriented was that direct object references created highly coupled systems that were very brittle. Direct references aggregate many constraints that are only important for a specific implementation. This problem was not fully solved until we got Java interfaces. Java interfaces allow a user of that interface and an implementer of that interface to only couple on the relevant details necessary for the collaboration; any implementation details on either side of the interface are not constraining the other side in any way.
+What the maligners do not see is that we face the exact same problem when we turn JAR into modules. The maven model only supports direct references between modules and they aggregate implementation constraints in the same way as object references did. The solution the OSGi came up with is to treat a package as a specification, just like a Java interface specifies the behavior of an implementation class.
+In this way a bundle (the OSGi JAR module) could provide an implementation for a (specification) package and other bundles could consume this implementation. Again, this is similar to the Java interface for classes.
+In the Java world we learned rather slowly that interfaces are great but that you have this pesky problem of how to get an instance for that interface if you should not be coupled to a specific implementation? Java created a a number of quite horrendous factory models for almost each subsystem, a factory model that too often bled into the actual collaborative API. For example, the javax.persistence package does not only contain the classes to work with JPA, it also contains a surprisingly large API to manage the life cycle of the provider. Factories also heavily abused class loaders for a purpose they were clearly not designed for. In Java 6 Sun tried to stop the hemorrhaging with the Service loader but forgot that class loaders are not designed for this purpose and baked them right into the API.
Spring brought dependency injection to the masses that allowed the consumers and providers to stay away from each other but it then introduced a massive coupling in the XML that contained too many details about the provider implementations since it specified the provider class names.
+The solution OSGi came up with in 1998 was to use a service broker. A broker makes sure that consumers and providers were bound together once they are available. This is the only true modular model that allows all implementation details to remain private inside the module.
+ +A the consequence of this model is that services, an implementation object implementing a Java interface from a specification package, cannot be assumed to be always there. Where Spring calculates the dependencies of its beans to initialize a system in the right order, a broker negotiates when a service is available since it does know any of its implementation details. One of those implementation details is when that service will be available. The broker is therefore dynamic, it can react to changing circumstances, another even more both maligned and misunderstood OSGi feature.
+The hardest part of OSGi is to look at it with a clean slate. The core model is surprisingly simple, elegant, and extremely powerful. The hard part is that it tends to conflict (as it should!) with all the bad habits in existing code bases that grew up thinking class loaders were simple extension mechanisms.
+So to summarize, a bundle is a JAR that imports and exports a set of packages. These imports and exports are bound to other bundles when they are resolved, allowing multiple versions of the same package. Once a bundle is started, it can then communicate with the external world at its own discretion; it can also get and register services to collaborate with other bundles. The following picture depicts the model of an OSGi application:
+ +The symbols used are defined in the OSGi specifications. The rectangle with rounded corners is a bundle, the triangle is a service (it always points in the dependency direction, i.e. the Reporter bundle depends on the sensor service to be there. Input and output is depicted with the corresponding flow chart symbol.
+In a perfect OSGi world, packages are just a minor detail. Until then, we also need some way to show package imports and package exports. In the OSGi specification, we use an open rectangle for imported packages and and black rectangle for exported packages. Private packages are depicted with a grey rectangle, see:
+ +There is often a confusion of terminology, like, is a bundle a component? We are guilty of not always using the terminology consistent. However, in the past few years it has become very clear that the OSGi declarative services provide a programming model that should have been incorporated in the OSGi framework from the beginning. DS allows you to make any object active with a simple annotation. This object can automatically be registered as a service. If there are dependencies on other services then you can easily specify those dependencies with an annotation on a set method.
+In DS, the implementation class is called the component and in this document we follow this lead. Therefore, in general a bundle consists of a number of components.
+The bndlib workspace is an encapsulation of a set of cohesive projects, where a project exports zero or more bundles via repositories. A repository provides access to set of bundles exported by some means, likely from other workspaces. A repository can be on the local file system or a remote system like Maven central.
+Projects can depend on other projects in the workspace or import bundles from the repositories. This is depicted in the following figure.
+ +A workspace is a single directory, just like a git workspace it encompasses all its sub directories. Though the name of the workspace directory is free to choose, it is highly recommended to use a naming strategy. In practice you will create many different workspaces and having a naming strategy will significantly simplify the handling of these workspaces.
+On the same root level in the workspace as the cnf directory, bndlib expects the projects. Yes, projects must reside in the root level. The reason is again, simplicity. We will later discuss how the bundle's symbolic name is derived from the project's directory name. Since projects can depend on each other, bndlib maintains a workspace repository of projects that it derives from the top level directories. Some people desperately want to use hierarchies of projects (often because that is how they used to work before). However, even people patching bndlib to make it hierarchical admit that the simple linear model is actually working quite well. The reason it works so well is that a workspace is not supposed to hold every single bundle that your organization produces. It is intended to be a cohesive set of between 10-20 up to a couple of hundred projects.
Bnd uses Java properties files (often with extension .bnd) for configuration purposes. Those usually contain headers or instructions. The files are read with java.util.Properties.load(Reader) with UTF-8 encoding and therefore support either of the following key value separators
= (default separator): (separator also used in MANIFEST.MF files)<white space character>In general any <white space> characters directly preceding or succeeding the separator are stripped.
OSGi is arguably one of the best specifications in the Java world. It is a no compromise specification for a component framework that is based on what we today know are the best practices. It extends the type safety first model of Java to hold true over time when the modules morph into sometimes unrecognizable shapes. It provides a solid foundation to build applications that can run anywhere, the original promise of Java; offering these impressive features while remaining as simple as possible, albeit no simpler.
+For some, the previous paragraph may come as a surprise because OSGi has had its share of people complaining about it. Surprisingly, the biggest complaint is often the Class Not Found Exception, which is always a perfect sign that people try to push a round peg in a too small square hole, and with all their might. You only see those exceptions when you're not doing engineering but when you are hacking. If you run head on into the walls that OSGi installs and it is giving you a headache, then just look around and find the elegant and easy to use doors: services.
+Though this is all true, I do not claim that OSGi is trivial to use; triviality has a way to clash with large system that must evolve over many years. The software profession is a brutal industry that lures us with the siren song of a 'few hours work' to devour us while trying to make gigantic hairballs. As Fred Brooks already said so many years ago in his seminal book 'The Mythical Man Month', there is no silver bullet. Even OSGi will require hard work to build evolvable systems. And though we cannot make building complex systems easy, bnd can at least make it easier (and considerably more fun).
+When we started building the OSGi specifications around 1998 it became clear we needed metadata to describe the bundles. We chose the manifest since this was already a well defined resource in the JAR, why invent the wheel? Actually, it was Anselm Baird, a Sun employee at the time, who had come up with this idea in his Java Embedded Server (JES). Basically, the manifest was a property file with benefits. We still consider this choice for embedded metadata an excellent idea. Looking at Maven where the metadata is separated over a surprising number of files. It is clear that the reduction in the number of files and potential errors caused by out-of-sync metadata is quite large. My favorite example of this additional complexity is when the SHA1 files necessary in Maven were also signed, an easy mistake but induced by unnecessary complexity. An OSGi bundle is a completely self described artifact.
+So who got bnd started? Well, when we wrote our bundles in the dawn of the new millenium we quickly discovered that transient dependencies, well, eh, sucked. Several of us had great experience in Object Oriented design, and though objects clearly had become mainstream they frustratingly had not provided the level of reusability that some had dreamed of. As Brian Foote shows, we thought we were building shiny castles but in the end built a big ball of mud. Where our predecessors of the structured programming era always hammered on low coupling and high cohesion we had ignored their wise lessons and got ourselves tangled up in a nice mess. Trying to reuse a class too often dragged in more unwanted stuff than the cat does in the whole year.
+The advantage of Java was that it had found an elegant solution to the scourge of transient dependencies: Java interfaces. An interface elegantly broke transitive dependencies while maintaining type safety at the cost of an indirection. A cost anyone should gladly bear for hairballs are quite distasteful. Surprisingly, while Java offered us this elegant solution to break transitive dependencies, most people in our industry were blindly committing themselves to the same route, just on another level, which actually would make the consequences even worse.
+What we discovered was that we could use the idea of interfaces in an object oriented environment also for modules. An interface is an instance of contract based programming and we found out that we could use the same between modules. The idea behind the OSGi service is that you do not let modules directly interact but that you use reified conduits between the modules so that you keep the modules, which are implementations, unconnected. What better concept in Java to express this contract between modules is there but the concept of a package?
+Originally devised as the Java modules, Java packages are heavily underappreciated in the Java world. Even highly expert users sometimes only appreciate the namescoping of the package, brushing over any accessibility issues arising out of the use of multiple classloaders. They are wrong, a package is a perfect module. That said, it is a perfect encapsulation to describe the contract that governs the collaboration between modules. An interface has a too small granularity for such a collaboration contract since it often requires additional interfaces like listeners and also helper classes. Packages, although they are too fine grained for deployment modules, are eminently suitable to define a contract to given the collaboration between deployment modules.
+So we had come up with the surprisingly well fitting dependency model: 'Look Ma, No Coupling!' But then using it became quite painful because it required a lot of tedious house keeping, tracking which packages you were using, of what version. Just bluntly expressing your dependency on an implementation sometimes felt as attractive as the next shot of heroin for a junkie. Now, housekeeping and chores in general are quite low on the list of reasons to give meaning to live for most software developers, and we were no exception. Not only are we in general horrible at chores, we tend to make an amazing amount of mistakes doing them. Which, in general is not good software engineering. Fortunately, we largely live in a virtual world where it is easy to let the computer do the chores. And this is the raison d'etre of bnd: take the chores out of OSGi and focus on the fun parts. And maybe even more important, 'Do Not Repeat Yourself'.
+The early version of bnd was called btool and was incepted at Ericsson, where it was used to automate the generation of the manifest in the ebox project. Then, in 2001, btool was used to automate the build process of the specifications, the reference implementations, and the test suites at the OSGi Alliance. One day there was a bit of confusion about the pedigree of the btool, and it was showing its age; it was time for a rewrite and bnd was born.
+The name came from bundle, then removing the vowels, and cutting it to three characters to reduce the typing on the command line. The pronunciation: we actually have no clue, whatever your preference is. B AND D, bind, whatever. You want to write it as bnd, Bnd, or BND, be our guest. We prefer to write it like bnd since we love the symmetry.
+Over the years bnd gained a lot of power because we needed a tool at the OSGi Alliance that could not just create good bundles, it also was heavily used in the test cases that required bad bundles. Of course this was an excellent test range for bnd and this synergy is still true today.
+In 2003, Eclipse adopted the OSGi specifications, which led to extensive discussions within both communities. For those focused on OSGi, the importance of package-level dependencies was already well established, while for the Eclipse community—coming from a background of direct, transitive module dependencies—the approach was less familiar. The outcome of these differences can still be seen in the existence of Require-Bundle and Fragment-Host.
+Beyond this, there were also differing perspectives on how bundles (referred to as “plugins” in Eclipse terminology) should be developed. Eclipse introduced tools such as PDE and later p2, which became central to their ecosystem. In contrast, the bnd experience emphasized that the OSGi manifest should not be maintained manually. Much like editing class files directly, manually editing manifests is error-prone. Although the manifest format is human-readable, it was never intended to be written by hand outside of exceptional circumstances.
+One design decision in OSGi was to keep the runtime as lightweight as possible, which was particularly important for embedded systems. This meant avoiding constructs that would complicate runtime behavior. For example, while wildcarded package exports could simplify authoring, they would require the framework at runtime to scan all classes to determine available packages—an inefficient and error-prone process. Similarly, the manifest inevitably contains both source and derived information, which can lead to redundancy if managed manually.
+The PDE approach, however, is based on a “manifest-first” model. In this approach, the manifest is not just a description of the build result but actively drives the build process. For instance, if a bundle is required in the manifest, PDE automatically adds it to the build classpath. Likewise, when a package is imported, PDE locates a bundle that exports that package and places it on the classpath, assuming a unique provider can be found in the target platform.
+In the meantime since 2024 Eclipse PDE now also offers an approach to automatically generate the Manifest, using bndlib under the hood.
+Inside the OSGi Alliance we were in a bind (pun intended) because PDE was not suitable for our own build. We used bnd in ant (it was also an ant plugin) but we missed the joy of Eclipse. This caused bnd to develop a split personality. Originally it was a JAR generator based on a small recipe but for our build we needed project & workspace concepts. We toyed with the idea to split it into a bld and a bnd tool but in the end even we committed the sin against modularity of low cohesion and kept these two tools in one out of laziness.
+Obviously we also developed PDE envy because Eclipse was actually awfully nice except for it. After toying with the idea of using the Eclipse metadata (.classpath) and finding out that this was only possible if we included the complete Eclipse IDE in an offline build, we embarked on developing an Eclipse plugin. Just not a good idea. So we reversed the model, and developed a library that had its own internal, uncoupled, model of a build. You see, the problem is that most build environments are quite pedantic and strongly optimized for their primary goal. Ant, maven, gradle, et. al. are just not easy to use inside an IDE because they are very stream driven: start, process, stop. In this model there is very little incentive to optimize incremental building and event notifications for important changes. An IDE is the reverse, it is start, build, build, build, ..., build, build, stop. For performance, it is crucial to optimize the building out of incremental changes to keep the IDE responsive. It is also crucial to send out events when important things happen. So we decided to pursue the middle ground: a model of projects and workspaces that was as uncoupled of the real world as possible but providing the hooks to use it in all popular build tools, either command line tools or IDEs. This is rather well captured in the expression: "The one tool that bnd's them all".
+Then one day we heard that Neil Bartlett also had started a plugin called bndtools. He had used the open source library of bnd and started to work on creating a pleasant to use friendly environment. Interestingly, he had created a continuous builder for bnd so that every save operation automatically build the bundles, something we had considered in our plugin but had been too afraid to do out of performance fear. Neil, however, was developing a lot of stuff that was already in bnd, he only used the JAR packager and manifest generator. After beating him up, which even took a special trip to the UK where we worked for 8 hours in a hotel lounge, he surrendered and thus our long term fruitful collaboration was born.
+Currently bnd(tools) is managed through a Github organization called bndtools and has its home site at https://bndtools.org. It is split in a number of repositories: bndtools, bnd, bndtools.rt and a number of associated support repositories. Though the collaboration between bndtools and bnd is awfully close, we are fanatic about separating the projects, our goal is still to allow Jetbrains to use bnd in IntelliJ without forcing them to eat any Eclipsisms. And it is always fun when hear someone claiming to successfully integrate bndlib in a product or tool, raving how uncoupled it was.
+What you are now reading is the manual of bnd, which is the result of a tremendous amount of work, and will be a work in progress for a long time to be. Though much of bnd's development was indirectly supported by the OSGi Alliance it is and always will be an open source project. This gave the authors the license to scratch their itches and not worry too much about documenting the nifty things they developed to scratch. Though the most important aspects were documented, it was sparse and not overly well organized. Though we always hoped someone with a gift for documenting would come along, fall in love with bnd, and write the perfect documentation, this somehow failed. After 16 years, we find it is time to take up this task ourselves, still praying that we will get support from bnd's surprisingly large (to some) group of users, don't hesitate. This is the reason this manual is a github repositories. Contributing is trivial, clone the bndtools/bnd.manual repo, edit the markdown text (you can even do this on the github web), save it, and create a pull request. Don't (always) ask what bnd can do for you, ask what you can do for bnd's users ...
+Not sure why we wrote such a long introduction, the facebook generation seems have an attention span of 5 lines, so we are probably among ourselves dear reader, congratulations on your stamina!
+ + diff --git a/org.bndtools.help/docs/chapters/140-best-practices.html b/org.bndtools.help/docs/chapters/140-best-practices.html new file mode 100644 index 0000000000..327d1f6c15 --- /dev/null +++ b/org.bndtools.help/docs/chapters/140-best-practices.html @@ -0,0 +1,32 @@ + + + + +The idea of a workspace is that it is cohesive. It contains a set of shared bundles and some of those bundles are exported to a repository. Other bundles are imported from repositories. This is the basic model of modularity.
+This strongly implies that you should NOT share cnf between workspace, just like you should not share private fields from classes nor private classes from packages. So the idea is that your organizations have a number of workspaces that contain the code that has a strong relation. For example a product, your company service APIs, or shared base libraries. Since all these projects are in one workspace you get a lot of ease of use like refactoring and immediate feedback.
+That said, there is often a desire to have some of the information, like the repositories in a shared place because you do not want to maintain it multiple times. Since bnd is completely based on inherited properties this is not that hard. The top of the properties are coming from the cnf/build.bnd file. Since this is a bnd file, you can actually include another bnd file there. Since this can be included via a URL, you can refer it to a file on your git repository. For convenience, you could make it refer to master but a better way is to make it refer to an actual commit. The reason is that if you checkout your project 5 years from now, it is unlikely that your build will be ok with the latest version of the included bnd file.
+build.bnd:
+
+-include: https://examplegit.com/foo/bar/master/shared/shared.bnd
+A more defined way to handle this long term versioning problem is to use git modules. They have a bad name but as far as I understand their bad name is because people don’t like that they are not automatically updated, which is exactly what you want when you want to build your project 5 years from now. Git modules require an explicit command to upgrade it to the latest or another version. The parent git repository stores the commit at which it is linked. With git modules you could make a subdirectory in cnf and then include a shared file from there:
+cnf
+ shared/
+ .git/
+ shared.bnd
+ build.bnd
+
+build.bnd:
+-include shared/shared.bnd
+So to summarize, share workspaces, not projects. Use continuous integration that publishes your bundles to a repository that is shared with all. Share one bnd file with a git module.
+ + diff --git a/org.bndtools.help/docs/chapters/150-build.html b/org.bndtools.help/docs/chapters/150-build.html new file mode 100644 index 0000000000..f0cc9a67cd --- /dev/null +++ b/org.bndtools.help/docs/chapters/150-build.html @@ -0,0 +1,197 @@ + + + + +This chapter lays out the rules of the file system for bnd projects. It discusses the workspace layout and the projects layout as well as the properties.
+A Workspace is a single directory with all its sub-directories and their files, similar to a git workspace. The core idea of the workspace is that it is easy to move around, that is, it allows the use of relative file names. It also prevents a lot of potential problems that occur when you allow projects to be anywhere on the file system. KISS!
+Workspaces should be named according to the bundle symbolic names of its projects. Using such a naming strategy will simplify finding the correct namespace given a bundle symbolic name.
+A bndlib workspace is a valid workspace when it contains a cnf file. If this is a text file, its content is read and interpreted as a path to the cnf directory (which can again be a path to a cnf directory, ad infinitum). The retrieved path is trimmed after which it is resolved relative to its parent directory.
However, the advised model is to use a directory with a cnf/build.bnd file. The purpose of the cnf directory is to provide a place for shared information. Though this includes bndlib setup information, it also can be used to define for example shared licensing, copyright, and vendor headers for your organization.
The cnf directory can have an ext directory. Files in this directory are added to the properties
+of the workspace. They can have the following extensions:
.bnd – Contain bnd properties.pmvn – An index file for a Maven Bnd Repository. The first lines can contain properties for this plugin in the format of # key = value, e.g. # name = OSGi R8. .pobr – An OSGi Repository file in XML.Files with .pmvn or .pobr will be automatically registered as a Repository and show up in the Repository browser (means, no Plugin configuration needed). This is a convenience shortcut to add a repository with almost no configuration.
Example /cnf/ext/central.pmvn file:
+# name = My Maven Central Repo
+# releaseUrl = https://repo.maven.apache.org/maven2/
+# tags = resolve
+biz.aQute.bnd:biz.aQute.bndlib:7.0.0
+biz.aQute.bnd:aQute.libg:7.0.0
+The ext directory is a convenient way to add add reusable components. See template fragments how they can be used to manage workspaces. When files change in this directory the workspace will be reloaded.
To cache some intermediate files, bndlib will create a cnf/cache/ directory, this file should not be under source control. E.g. in Git it should be defined in the .gitignore file.
Overall, this gives us the following layout for a workspace:
+com.acme.prime/ workspace
+ cnf/ configuration/setup
+ ext/ extensions
+ maven.bnd maven setup extension
+ build.bnd organization setup
+ plugins/ directory for plugins
+ cache/ bnd cache directory, should not be saved in an scm
+ com.acme.prime.speaker/ project directory
+The root of the workspace is generally used to hold other files. For example the .git directory for Git, or gradle and ant files for continuous integration. However, designers that add functionality to the workspace should strive to minimize the clutter in the root. For example, the bnd gradle support adds a few files to the root but these link to a cnf/gradle directory for their actual content.
Other directories in the workspace represent projects. The name of the project is the bundle symbolic name of the bundle that it produces (or the prefix of the bundle symbolic name when it produces multiple bundles).
+Properties are used for headers, macros, and instructions in bndlib, they are quite fundamental. To simplify maintenance, bndlib provides an elaborate mechanism to provide these properties from different places and inherit them. The workspace resides at the top of this inheritance chain (ok, after the built-in defaults).
+When a workspace is created, it will first read in the properties in the .bnd files in the cnf/ext directory. These are called the extension files since they in general setup plugins and other extensions. The order in which they are read is the lexical sorting order of their file names.
The target-dir defines where the build process places the build output artifacts. By default this folder is named generated.
In the default setup of the workspace, the gradle build tool & eclipse share the same output directories. In general, this means you always have to clean in each tool and in the case of Eclipse stop the incremental builder (otherwise eclipse will start rebuilding the whole workspace when you do a gradle clean build).
All the output directories are defined in macros. In general, the ${target-dir} is the main output directory and the ${bin} and ${testbin} are placed inside this directory. So by redefining ${target-dir} in cnf/build.bnd we can redirect all output.
bnd has a macro ${driver} that indicates which build tool (a.k.a. the driver) is used. We can then use this as follows, to use a separate output directory for each build tool (e.g. eclipse, gradle, maven):
``` +target-dir generated${if;${driver;eclipse};;/${driver}} +bin ${target-dir}/classes +testbin ${target-dir}/test-classes +````
+This example configuration, placed in cnf/build.bnd means:
generated folder of each project.generated/gradle. Extension files allow you to separate configuration concerns. Its primary purpose is to allow third party extensions. These extensions can then put their properties in one place. The contents of these files should therefore not be touched so that a new version can override them. Each extension file is read as a bnd file, this means that full power of bndlib is available. The bnd command line tool has facilities to add and remove files from this directory.
+For example, the Maven plugin that is built-in to bndlib has an extension file called maven.bnd which looks as follows:
+#
+# Plugin maven setup
+#
+-plugin.maven = aQute.bnd.plugin.maven.MavenPlugin
+
+
+#
+# Change disk layout to fit maven
+#
+
+-outputmask = ${@bsn}-${versionmask;===S;${@version}}.jar
+src=src/main/java
+bin=target/classes
+testsrc=src/test/java
+testbin=target/test-classes
+target-dir=target
+We will not explain this plugin here (you can find it in the plugin sections), it only illustrates here how it is possible to setup the environment for a specific optional functionality like a plugin.
+If you create local extension files then you should use a prefix to identify this is your file, like:
+com.acme-local.bnd
+It is possible to use file links to maintain these files in one place when you have many workspaces.
+Extension files will be loaded in alphabetical order. If a files defines a property that was already defined in another previously loaded file or the build.bnd, the property will be assigned a namespace. -plugin in /cnf/ext/test.bnd will become -plugin.test. Please note that this mechanism is just a best guess courtesy and has its limits. If -plugin.test was already defined previously, the old value will be overwritten.
After reading the extension files, bndlib reads the cnf/build.bnd file, this file is supposed to hold the organization specific properties. Out of the box, this file comes empty, ready to be filled by you.
A plugin is a piece of code that runs inside bnd. The workspace provides a number of standard built-in plugins like an Executor, a Randum number generator, itself, etc. Additional plugins can be added with the -plugin.* instructions.
There are a number of built in properties that are set by bnd:
+|-----------------+-----------------------------------------------------------------|
+| Property name | Description |
+|-----------------|:----------------------------------------------------------------|
+|project | Name of the project. This is the name of the bnd file without |
+| | the .bnd extension. If this name is bnd.bnd, then the directory |
+| | name is used. |
+|-----------------+-----------------------------------------------------------------|
+|project.file | Absolute path of the main bnd file. |
+|-----------------+-----------------------------------------------------------------|
+|project.name | Just the name part of the file path |
+|-----------------+-----------------------------------------------------------------|
+|project.dir | The absolute path of the directory in which the bnd file resides. |
+|-----------------+-----------------------------------------------------------------|
Run instructions are used to start OSGi tests and OSGi runs.
+|-runbundles |LIST SPEC |Additional bundles that will be installed and started when the framework is launched. This property is normally part of the project's bnd.bnd file.|
+|-runvm |PROPERTIES |Properties given to the VM before launching. This property is normally set in the cnf/build.bnd file and only in rare cases overridden in the bnd.bnd file.|
+|-runproperties |PROPERTIES |Properties given to the framework when launching. Usually project specific.|
+|-runsystempackages|PACKAGES |A declaration like Import-Package that specifies additional system packages to import from the class path. Usually given in the cnf/build.bnd file.|
+|-runpath |LIST SPEC |A path description of artifacts that must be on the classpath of the to be launched framework. Usually given in the cnf/build.bnd file. This path should contain the framework. Any packages that a bundle on the -runpath should specify should be listed in the export attribute.|
+|-runtrace |true|false |Trace the startup of the framework to the console. Usually used during testing and development so project specific.|
+|-runframework |none|services|NONE indicates that a mini built in framework should be used. SERVICES indicates that the META-INF/services model must be followed for the org.osgi.framework.launch.FrameworkFactory class. Project specific.|
+|-testpath |LIST SPEC |A path used to specify the test plugin.|
Launching is needed when the project's run action or test action is executed. The project creates a Project Launcher. A Project Launcher must launch a new VM and set up this VM correctly. The VM is launched with the following information:
java - The command to launch a new VM is by default java. However, this can be overridden by setting a property called java.classpath - The classpath set for the VM is derived from the -runpath property. Notice that this is supposed to contain the JAR with the framework. The -runpath requires bundle symbolic names for the JAR and an optional version range. bnd will use the latest version found in the repository. Any packages that should be exported by the system bundle should have an export attribute containing the exported packages, like junit.osgi;version=3.8;export="junit.framework;version=3.8,junit.misc;version=3.8".-runvm property. They are usually in the form of -Dxya=15 or -X:agent=bla. Options should be separated by commas.main - The class implementing the main type is defined by the launcher plugin.An example of a launcher set is:
+-runvm: -Xmn100M, -Xms500M, -Xmx500M
+-runpath: \
+ org.apache.felix.framework; version=3.0, \
+ junit.osgi;export="junit.framework;version=3.8"
+-runtrace: true
+-runproperties: launch=42, trace.xyz=true
+-runbundles: org.apache.felix.configadmin,\
+ org.apache.felix.log,\
+ org.apache.felix.scr,\
+ org.apache.felix.http.jetty,\
+ org.apache.felix.metatype,\
+ org.apache.felix.webconsole
+Debugging the launcher is greatly simplified with the -runtrace property set to true. This provides a lot of feedback what the launcher is doing.
When the Framework is started the Launcher will register itself as a service of type java.lang.Object with the property launcher.arguments to provide access the arguments handed to the launcher. After all Bundles have been installed and started the ServiceRegistration will be updated with the property launcher.ready=true.
In certain cases it is necessary to grab the main thread after launching. The default launcher will launch all the bundles and then wait for any of those bundles to register a Runnable service with a service property main.thread=true. If such service is registered, the launcher will call the run method and exit when this method returns.
The launcher will timeout after an hour. There is currently no way to override this timeout.
+The bnd launcher contains a mini framework that implements the bare bones of an OSGi framework. The purpose of this mini framework is to allow tests and runs that want to launch their own framework. A launch that wants to take advantage of this can launch with the following property:
+-runframework: none
+In Ant, the following task provides the run facility.
+<target name="run" depends="compile">
+ <bnd command="run" exceptions="true" basedir="${project}" />
+</target>
+These targets provide commands for ant run.
Testing is in principle the same as launching, it actually uses the launcher. Testing commences with the test action in the project. This creates a Project Tester. bnd carries a default Project Tester but this can be overridden.
The basic model of the default Project Tester plugin is to look for bundles that have a Test-Cases manifest header after launching. The value of this header is a comma separated list of JUnit test case class names. For example:
Test-Cases: test.LaunchTest, test.OtherTest
+Maintaining this list can be cumbersome and for that reason the ${classes} macro can be used to calculate its contents:
Test-Cases: ${classes;extending;junit.framework.TestCase;concrete}
+See classes macro for more information.
+<target name="test" depends="compile">
+ <bnd command="test" exceptions="true" basedir="${project}" />
+</target>
+Both the Project Launcher and Project Tester are plugins. Defaults are provided by bnd itself (bnd carries a mini cache repo that is expanded in the cnf directory), it is possible to add new launchers and testers as needed.
Launcher or tester plugins are found on the -runpath or the -testpath properties, respectively. To detect a plugin, bnd will look for an appropriate manifest header. The header value is a class name. Bnd will then instantiate the class and use it as a launcher/tester. The classes must extend an abstract base class. Each plugin has access to the Project object, containing all the details of the project.
|Plugin |Manifest header|Base Class |Where searched| +|------------------------------------------------------------------| +|Project Launcher |Launcher-Plugin|ProjectLauncher|-runpath| +|Project Tester |Tester-Plugin |ProjectLauncher|-testpath|
+The plugin gets complete control and can implement many different strategies.
+ + diff --git a/org.bndtools.help/docs/chapters/155-project-setup.html b/org.bndtools.help/docs/chapters/155-project-setup.html new file mode 100644 index 0000000000..429d7f23f3 --- /dev/null +++ b/org.bndtools.help/docs/chapters/155-project-setup.html @@ -0,0 +1,12 @@ + + + + +This is about generating OSGi JARs - one of the main tasks of bnd.
+This is a simple example of wrapping a jar with bnd (wrapping means: taking a non-OSGi jar and use bnd to add OSGi meta data to its MANIFEST.MF to get a proper OSGi .jar).
The basic idea is to create a recipe (a .bnd file) that collects the different resources in the right way to create the new output .jar including the OSGi meta data in MANIFEST.MF.
For example, you want to wrap the WebSocket server from https://github.com/TooTallNate/Java-WebSocket/releases/tag/Java-WebSocket-1.3.4. Download the binary (Java-WebSocket-1.3.4.jar) and the sources in Java-WebSocket-1.3.4-sources.jar.
+Once you have these files, create a folder e.g. mkdir wrappers and add bnd.bnd file in the project to create the org.websocket.jar bundle.
# bnd.bnd
+# Wrapped version of Github project TooTallNate/Java-WebSocket
+Bundle-SymbolicName: org.websocket
+Bundle-DocURL: https://github.com/TooTallNate/Java-WebSocket
+Bundle-License: https://github.com/TooTallNate/Java-WebSocket/blob/8ef67b46ecc927d5521849dcc2d85d10f9789c20/LICENSE
+Bundle-Description: This repository contains a barebones \
+ WebSocket server and client implementation written \
+ in 100% Java. The underlying classes are implemented \
+ using the Java ServerSocketChannel and SocketChannel \
+ classes, which allows for a non-blocking event-driven model \
+ (similar to the WebSocket API for web browsers). \
+ Implemented WebSocket protocol versions are: Hixie 75, \
+ Hixie 76, Hybi 10, and Hybi 17
+
+# version taken from the downloaded file above
+Bundle-Version: 1.3.4
+
+-includeresource: @Java-WebSocket-1.3.4.jar, OSGI-OPT/src=@Java-WebSocket-1.3.4-sources.jar
+-exportcontents: org.java_websocket
+Either use the bndtools Eclipse plugin or the bnd CLI to process the bnd.bnd file and let bnd create the .jar. In case of bnd CLI the command is:
bnd bnd.bnd
+Applying this recipe above gives the following META-INF/MANIFEST.MF in a JAR named /generated/org.websocket.jar:
Manifest-Version: 1.0
+Bnd-LastModified: 1338190175969
+Bundle-Description: This repository contains a barebones WebSocket serve
+ r and client implementation written in 100% Java. The underlying classe
+ s are implemented using the Java ServerSocketChannel and SocketChannel
+ classes, which allows for a non-blocking event-driven model (similar to
+ the WebSocket API for web browsers). Implemented WebSocket protocol ve
+ rsions are: Hixie 75, Hixie 76, Hybi 10, and Hybi 17
+Bundle-DocURL: https://github.com/TooTallNate/Java-WebSocket
+Bundle-License: https://github.com/TooTallNate/Java-WebSocket/blob/8ef67
+ b46ecc927d5521849dcc2d85d10f9789c20/LICENSE
+Bundle-ManifestVersion: 2
+Bundle-Name: org.websocket
+Bundle-SymbolicName: org.websocket
+Bundle-Version: 1.3.4
+Export-Package: org.java_websocket;version="1.3.4";uses:="javax.net.ss
+l"
+Import-Package: javax.net.ssl
+Private-Package: org.java_websocket.client,org.java_websocket.drafts,o
+rg.java_websocket.exceptions,org.java_websocket.framing,org.java_webs
+ocket.handshake,org.java_websocket.server,org.java_websocket.util
+Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
+You notice that the .bnd file and the MANIFEST look similar but the bnd instructions caused bnd to generate headers like Export-Package and Import-Package. bnd did that by analysing the .class files inside Java-WebSocket-1.3.4.jar (e.g. by looking at all the import statements classes, methods, method parameters and return types).
Note: The documentation headers like Bundle-Description are optional but very important, just spent the minute to document them since you'll be grateful later.
If the target project does not have a version, makeup a version and maintain it. Notice that in general the recipe will only be used once for each version, it is normally not used in continuous integration builds. However, you normally use it to convert the next version of the project. Crisp versioning is important.
+The -includeresource statement unrolls the jars we downloaded in the root of the JAR and in OSGI-OPT. Since the source code is in the src directory in the WebSocket-src.zip file, we put it in the new JAR under OSGI-OPT/src. This convention is supported by all IDEs to give you direct access to the bundle's source code. Since the binary and the source are kept together, you always have the correct source code available, and usually automatically. It is so convenient that once you're used to this it is hard to imagine a life without source code.
The binaries and sources are not in the final jar but bnd does not yet know what needs to be exported. This can be indicated with the -exportcontents instruction. It has the same syntax as Export-Package but does not copy from the classpath, it only applies the instruction to the content of the final JAR.
When bnd processes OSGi manifest headers, it automatically ensures consistent ordering of attributes and directives within header clauses. This ordering is applied to OSGi syntax headers such as Export-Package, Import-Package, Require-Capability, Provide-Capability, and others.
Bnd applies the following ordering rules to attributes and directives within OSGi header clauses:
+Given an input header like:
+Export-Package: com.example.api;uses:="com.example.internal";version=1.0.0;mandatory:="version";provider=acme
+Bnd will reorder it to:
+Export-Package: com.example.api;provider=acme;version=1.0.0;mandatory:="version";uses:="com.example.internal"
+Notice how:
+- Attributes (provider, version) come first, sorted alphabetically
+- Directives (mandatory:, uses:) come second, sorted alphabetically
This consistent ordering provides several benefits:
+This ordering is applied to all headers defined in Constants.OSGI_SYNTAX_HEADERS, which includes:
+- Export-Package
+- Import-Package
+- Require-Capability
+- Provide-Capability
+- Bundle-SymbolicName
+- Fragment-Host
+- And other OSGi-defined headers that use the clause syntax
One of the great features of bnd is to use export version from other versions to generate the import ranges. This feature requires that the other JARs are on the classpath. In bndtools you can use the -buildpath. However, you always add entries on the class path per bnd descriptor with the -classpath instruction:
+-classpath: dependency.jar, other.jar
+The easiest way to build these wrappers is to create a project in bndtools called wrappers and create a main bnd.bnd with the following content:
-sub: *.bnd
+The create a bnd.bnd descriptor for each wrapper you need to build. e.g.
websocket.bndsomeotherlib.bndWhen the main bnd.bnd is processed bnd creates a jar for each sub- .bnd file.
+E.g.
bnd bnd.bnd
+creates a /generated/websocket.jar and /generated/someotherlib.jar.
All headers are listed on the Headers index
+ + diff --git a/org.bndtools.help/docs/chapters/170-versioning.html b/org.bndtools.help/docs/chapters/170-versioning.html new file mode 100644 index 0000000000..d5beba31cb --- /dev/null +++ b/org.bndtools.help/docs/chapters/170-versioning.html @@ -0,0 +1,165 @@ + + + + +Versioning is probably the most painful part of developing real software. Where toys and prototypes can be developed ignoring evolution, real software requires a migration path to an unknown future.
+The OSGi has defined a versioning policy that is described in the Semantic Versioning whitepaper. bnd fully supports this model and provides many shortcuts. The goal of bnd is remove any manual work from versioning bundles as well as packages.
+The key concept to version in OSGi is the ''package''. Bundles are an ''aggregate'' and therefore must move as fast as the fastest moving exported packages they contain. For example, if a bundle contains two exported packages foo and bar and foo is not changed but bar has a major change, then the bundle version needs to also have a major change. This requires an unnecessary update for a bundle that only depended on foo. Aggregating dependencies increases the fan out of the transitive dependencies. The result is that systems can only evolve when everything is updated simultaneously. The result is that the system as a whole becomes brittle.
In contrast, versioning the packages and using Import-Package, bundles can be refactored and versioned independently.
+provide:=true directive on the package export, e.g. Export-Package: org.osgi.service.event; provide:=true.A version in OSGi has 4 parts:
+major 1 + minor 1.1 + micro 1.1.1 + qualifier 1.1.1.qualifier
+To survive versioning, one must have a ''version policy''. A version policy puts semantics on the version numbers. The ''recommended'' policy in OSGi is changing the part when there is:
+major a breaking change + minor a backward compatible changes + micro a bug fix (no API change) + qualifier a new build
+In OSGi, the decision was taken to have a single export version. The import statement allows a version range to be set. For example:
+Export-Package: com.acme.foo; version=1.0.2
+Import-Package: com.acme.bar; version="[1,2)"
+The semantic versioning white paper introduces two terms that are orthogonal to the imports and exports as well as implementing or delegating:
+Provide and consume is orthogonal to implementing an interface and delegating. For example, the Configuration Admin service has the ConfigurationAdmin interface that is implemented by the Provider of an API but the ConfigurationListener interface is implemented by the Consumer of the API.
The reason for the providers and consumer terms is that version policies are different. A change in an API almost always affects the provider but with careful API design it is often possible to make a change backward compatible for consumers.
+In bnd, whenever you have to provide an import range, you can use modifiers to create a range out of a single version:
+@1.2.3 – Creates an import up to the next major version: [1.2.3,2.0.0).1.2.3@ – Creates an import up to the next minor version: [1.2.3,1.3.0).=1.2.3 – Creates a range that only accepts that version: [1.2.3,1.2.3].If you have a package that is containing implementation code that is supposed to be directly used by the consumers then this is a ''library''.
+A library package is not an API that can be implemented by other bundles, it is the implementation. Then the versioning of library packages is relatively straightforward: Any change that breaks a consumer of that package must increment the major version number. For example, if the popular ASM library would add a method to the MethodVisitor class then it must increment the major version number of the org.objectweb.asm package because all existing consumers of this library would then be broken.
If however a package contains an API that is provided and consumed by others the situation is more complex. In such a case, the provider should export the API package and the consumers should import it.
+bnd explicitly allows the inclusion of packages that come from other projects. It is just good practice to include an API package in your bundle if you are the provider of that API. However, this means that maintaining the version of the package in the manifest is ''wrong'', it would have to be maintained in several places, which is very error prone.
+For this reason, bnd provides a way to store the version of the package together with the package itself. One with annotations and one without when annotations are not possible.
+The @Version annotation is placed on the package. Since Java 5 it is possible to create a package-info.java file that can be used to annotate packages:
+package-info.java:
+ @org.osgi.annotation.versioning.Version.Version("1.2.0")
+ package com.example;
+A non-annotation based alternative is the packageinfo file. When bnd scans the Java archives it will look in each package for this packageinfo file. The format of this file is very simple:
packageinfo:
+ version 1.2.0
+If you now export the package (from any bundle that has the package on its class path), it will be properly versioned.
+bnd.bnd:
+ Export-Package: com.example.*
+The resulting manifest will look like:
+Manifest:
+ Export-Package: com.example; version=1.2.0
+If you export a package from another bundle, bnd will also look in the manifest of that other bundle for a version.
+Using packageinfo (or the @Version annotation) is highly recommended.
+The package version for package p can come from the following places (in order of increasing priority):
+p/packageinfop/package-info.java with an annotationThe bnd warning means that bnd finds multiple definitions of the version for p and they are not the same. So I assume you set the version in the manifest of the bundle under construction and in one of those other places. The best is to remove the version from your bnd.bnd file. The by far absolute best way is to only set the version of the package in the package directory (either packageinfo or package-info.java with an annotation).
If you import a package bnd will look at the exported version of that package. This version is not directly suitable for the import because it is usually too specific, it needs a policy to convert this export version to an import version.
+An importer that provides the functionality of an API package is much closer tied to that package than a client. The whitepaper recommends binding to the major.minor part of the version for a provider. That is, any change in the minor part of the version breaks the compatibility. This makes sense, the provider of an API must implement the contract and is therefore not backward compatible for any change in the API. A consumer of the API only has to be bound to the major part because it is much more relaxed for the backward compatibility.
+For example, a new method is added to an interface that is implemented by the provider of the API. Old clients have no visibility of this method because when they compiled it did not exist. However, the provider of the API must be modified to implement this method otherwise more modern clients would break.
+This asymmetry creates the need for two version policies:
+-provider-policy : ${range;[==,=+)}
+-consumer-policy : ${range;[==,+)}
+The given values are the defaults. The value of the version policy will be used calculate the import based on the exported package. The ${range} macro provides a convenient shortcut to do this using a version mask.
For example, a bundle that implements the OSGi Event Admin service can use the following bnd file:
+bnd.bnd:
+ Private-Package: com.example.impl.event
+The resulting manifest would look like:
+Manifest:
+ Import-Package: org.osgi.service.event; version="[1.1,2)", ...
+ ...
+How does bnd know if a bundle is a provider or a consumer of a specific package?
+Well, the default is the consumer policy but this can be overridden with the provide:=true directive that works on the Import-Package clauses as well as on the Export-Package clauses.
The provide: directive indicates to bnd that the given package contains API that is provided by this bundle. The (strongly) recommended way is to put the provide:=true directive on the Export-Package header, even if the package comes from another bundle. This way the bundle contains a copy of the package that is by default imported with the proper provider policy range.
Also see -consumerpolicy, -providerpolicy and range.
+For example, an implementation of the OSGi Event Admin specification could use the following bnd file:
+bnd.bnd:
+ Export-Package: org.osgi.service.event; provide:=true
+ Private-Package: com.example.impl.event
+The resulting manifest would look like:
+Manifest:
+ Export-Package: org.osgi.service.event; version=1.1
+ Import-Package: org.osgi.service.event; version="[1.1,1.2)", ...
+ ...
+If for some reason it is not desirable to export the API package in the implementation bundle, then the provide: directive can also be applied on the Import-Package header:
bnd.bnd
+ Import-Package: org.osgi.service.event; provide:=true, *
+ Private-Package: com.example.impl.event
+The resulting manifest would look like:
+Manifest:
+ Import-Package: org.osgi.service.event; version="[1.1,1.2)", ...
+ ...
+A key aspect of OSGi is that a package can be both imported and exported. The reason is that this feature allows a framework more leeway during resolving without creating multiple unconnected class spaces.
+After the bundle has been created and analyzed bnd will see if an exported package is eligible for import. An export is automatically imported when the following are true:
+-noimport: directive.If a package is imported it will use the version as defined by the version policy.
+With the -nosubstitution: true instruction, this behavior can be disabled globally.
+Versioning bundles usually requires bumping the version every time it is placed in a repository. When package versioning is used, the bundle version is only important for tracking an artifact.
+The micro is left out because it generates a lot of unnecessary releases, this is similar to the maven release process. If you connect everything 100%, you cannot move anything unless all its dependencies are moved at the same time. We actually tried in the OSGi build to use micro version changes for default methods in Java 8 but found that it just creates an enormous ripple through effect in the build. Not depending on the micro version is a lubricant that does not kill any bundle out there that depends on you.
+This should not be a problem because a micro version is a deployment issue since the semantic versioning should be used for APIs and a micro change is not visible in the API.
+That said, this is bnd so obviously you can override it. You can override the default version policy is:
+-provider-policy = ${range;[==,=+)}
+-consumer-policy = ${range;[==,+)}
+Just set '===' instead of '==' for the floor version in your pom.xml in the
<configuration>
+ <_provider-policy>${range;[===,=+)}</_provider-policy>
+ <_consumer-policy>${range;[===,+)}</_consumer-policy>
+</configuration>
+Baselining compares a bundle with another bundle, the baseline, to find mistakes in the semantic versioning. For example, there is a binary incompatible change in the new bundle but the version has not been bumped. Baselining can be run from the command line (see bnd help baseline) or it can be run as part of a project build.
APIs are compared for backward compatibility using the semantic versioning rules defined in this chapter. Baselining is aware of the @ConsumerType and @ProviderType rules. Proper versions are calculated and suggested.
During the build, bnd will check the -baseline instruction at the end of the build when the JAR is ready. This instruction is a selector on the symbolic name of the building bundle. If it matches, the baselining is started with one exception: by default, the bundle/package version must be 1.0.0 or above. If the version is less (i.e. major version being 0) no baselining errors are reported, the purpose is to allow the primordial baseline to be established without errors.
To enable baselining for versions in the range [0.1.0, 1.0.0), use the -baselineincludezeromajor instruction:
-baselineincludezeromajor: true
+This will enable baseline error reporting for packages with major version 0 (except for 0.0.x versions which are still excluded).
By default the baseline is a bundle from one of the repositories with the same symbolic name as the building bundle and the highest possible version. However, it is possible to specify the version or to baseline against a file.
+It is recommended to enable baselining per project since not all project requires baselining. For example, baselining is overkill for a project that is always compiled with all its dependencies and thus has no external dependencies. The recommended practice is therefore to add the following instruction to a project that requires baselining (usually API bundles):
+-baseline: *
+The default baseline is the bundle with the highest version in the baseline repository. The selector can specify a version or a file attribute when the default baseline is not applicable.
version – Baseline against the first bundle in the baseline repository that has the given version or higher.file – The file is the baseline.The baseline bundle is looked up in the baseline repository. The baseline repository is by default the release repository unless overridden by the -baselinerepo instruction. The release repository is set with the -releaserepo instruction.
+Only bundles that are not staging are considered for the baseline, this means that it is possible to release the current bundle and compare against the previous version until the bundle is released and becomes master.
+By default the bundle and baseline are compared (diffed) and then analyzed for semantic version violations. Certain headers always change because they contain time or digest information. Most of these headers are already automatically ignored but the -diffignore instruction can add more ignorance. You can use the +-diffpackages instruction to specify the names of exported packages to be baseline compared. The default is to baseline compare all exported packages.
+There is a dedicated Maven plugin for baselining.
+ Bundle-Version: 1.0.2
+ -baseline: *
+ -baselinerepo: Released
+In this example, the last version in the Released repository for the project's bsn is supposed to be the previous version. Make sure you do not always release staging versions to this repository since this will create false changes. During a development cycle, the baseline version must remain constant until the current development bundle is released, at which points it becomes the baseline of the next cycle.
+Since an error is raised when the baselining detects a semantic version violation it is possible to release a snapshot in a build only when there is a correctly baselined bundle built.
+@BaselineIgnore - Fine grained controlOccasionally, scenarios arise where a language construct or change is not accounted for in bnd (bnd developers are humans too), or where a developer wants to overrule the strict opinion of the baseline logic for whatever reason (not recommended but it does happen) for instance in the grey areas of binary compatibility.
+Instead of forcing developers to make a choice between disabling baseline (in order to avoid build warnings or failures) and keeping it enabled it's important to know that there is fine grained control available using bnd's @aQute.bnd.annotation.baseline.BaselineIgnore annotation.
The value of the @BaselineIgnore annotation is a valid OSGi version string.
e.g.
+@BaselineIgnore("2.4.12")
+public Foo getFoo();
+When the @BaselineIgnore annotation is applied to a baselined element, the baseliner will ignore the annotated element when baselining against a baseline package whose version is less than the specified version. This means the annotated element will not produce a baselining mismatch. The correct baseline information about the element will be in the baseline report, but the element will not cause baselining to fail. When baselining against a baseline package whose version is greater than or equal to the specified version, this annotation is ignored and the annotated element will be included in the baselining.
The annotation should be used in a scope that is as narrow as possible by applying it to the most specific member causing the baseline issue.
+ + diff --git a/org.bndtools.help/docs/chapters/200-components.html b/org.bndtools.help/docs/chapters/200-components.html new file mode 100644 index 0000000000..ac06e697b8 --- /dev/null +++ b/org.bndtools.help/docs/chapters/200-components.html @@ -0,0 +1,320 @@ + + + + +The Service-Component header is compatible with the standard OSGi header syntax. Any element in the list that does not have attributes must have a resource in the JAR and is copied as is to the manifest. However, simple components can also be defined inline, and it is even possible to pickup annotations.
+The syntax for these component definitions is:
+component ::= <name> ( ';' parameter ) *
+parameter ::= provide | reference | multiple | optional
+ | reference | properties | factory | servicefactory
+ | immediate | enabled | implementation
+ | activate | deactivate | modified | configuration-policy
+ | version | designate
+
+reference ::= <name> '=' <interface-class>
+ ( '(' <target-filter> ')')? cardinality?
+cardinality ::= '?' | '*' | '+' | '~'
+provide ::= 'provide:=' LIST
+multiple ::= 'multiple:=' LIST
+optional ::= 'optional:=' LIST
+dynamic ::= 'dynamic:=' LIST
+designate ::= ( 'designate' | 'designateFactory' ) CLASS
+factory ::= 'factory:=' true | false
+servicefactory := 'servicefactory:=' true | false
+immediate ::= 'immediate:=' true | false
+enabled ::= 'enabled:=' true | false
+configuration-policy ::= "configuration-policy:='
+ ( 'optional' | 'require' | 'ignore' )
+activate ::= 'activate:=' METHOD
+modified ::= 'modified:=' METHOD
+deactivate::= 'deactivate:=' METHOD
+implementation::= 'implementation:=' <implementation-class>
+properties::= 'properties:=' key '=' value
+ ( ',' key '=' value ) *
+key ::= NAME (( '@' | ':' ) type )?
+value ::= value ( '|' value )*
+If the name of the component maps to a resource, or ends in XML, or there are attributes set, then that clause is copied to the output Service-Component header.
+If the name can be expanded to one or more classes that have component annotations (they must be inside the JAR), then each of those classes is analyzed for its component annotations. These annotations are then merged with the attributes from the header, where the header attributes override annotations. The expansion uses the normal wildcard rules. For example, biz.aQute.components.* will search for component annotated classes in the biz.aQute.components package or one of its descendants. The classes must be present in the JAR. If no classes with annotations can be found for the name then it is assumed to be name or implementation class name without annotations.
The name of the component is also the implementation class (unless overridden by the implementation: directive). It is then followed with a number of references and directives. A reference defines a name that can be used with the locateService method from the ComponentContext class. If the name starts with a lower case character, it is assume to be a bean property. In that case the reference is augmented with a set<Name> and unset<Name> method according to the standard bean rules. Bnd will interpret the header, read the annotations if possible, and create the corresponding resources in the output jar under the name OSGI-INF/<id>.xml.
Annotations are only recognized on the component class, super classes are not inspected for the components.
+For a class to be a valid DS component, it must meet the following requirements:
+A DS component class must have either:
+- A public no-argument constructor (this is the default constructor if no other constructors are declared), OR
+- A public constructor annotated with @Activate for constructor injection
If your component class has a constructor with parameters but no @Activate annotation, bnd will generate an error:
The DS component class {className} must be publicly accessible and have either a public no-arg constructor or a public constructor annotated with @Activate. Non-public classes, including public inner classes enclosed in non-public classes, are not supported.
+Examples of valid components:
+// Valid: Implicit public no-arg constructor
+@Component
+public class MyComponent {
+ // Implicit public no-arg constructor
+}
+
+// Valid: Explicit public no-arg constructor
+@Component
+public class MyComponent {
+ public MyComponent() {
+ // initialization
+ }
+}
+
+// Valid: Constructor injection with @Activate
+@Component
+public class MyComponent {
+ private final BundleContext context;
+
+ @Activate
+ public MyComponent(BundleContext context) {
+ this.context = context;
+ }
+}
+
+// Valid: Multiple constructors with public no-arg
+@Component
+public class MyComponent {
+ public MyComponent() {
+ // default constructor
+ }
+
+ // This constructor is ignored by DS
+ public MyComponent(String config) {
+ // some other constructor
+ }
+}
+Examples of invalid components:
+// Invalid: No public no-arg constructor and no @Activate
+@Component
+public class MyComponent {
+ public MyComponent(BundleContext context) {
+ // ERROR: needs @Activate annotation
+ }
+}
+
+// Invalid: Package-private class (constructor not public)
+@Component
+class MyComponent {
+ // ERROR: class must be public
+}
+If you use inner classes as components, they must be declared as static. Non-static inner classes require an instance of the outer class to be instantiated, which DS cannot provide.
Valid:
+@Component
+public class OuterComponent {
+
+ @Component
+ public static class InnerComponent {
+ // Valid: static inner class
+ }
+}
+Invalid:
+@Component
+public class OuterComponent {
+
+ @Component
+ public class InnerComponent {
+ // ERROR: non-static inner class cannot be instantiated by DS
+ }
+}
+The supported annotations in the aQute.bnd.annotations.component package are:
||!Component|| +Annotated the class, indicates this class is required to be a component. It has the following properties:
+|| provide||Class[]||Service interfaces, the default is all directly implemented interfaces||
+||name ||String||Name of the component||
+||factory ||Boolean||Factory component||
+||servicefactory||Boolean||Service Factory||
+||immediate||Boolean||Immediate activation||
+||designate||CLASS||Designate a class as a [[MetaType]] interface used for configurations for unitary configurations, see [[#metatype]]. This changes the default of the configurationPolicy to require.||
+||designateFactory||CLASS||Designate a class as a [[MetaType]] interface used for configurations for factory configurations, see [[#metatype]]. This changes the default of the configurationPolicy to require.||
+||configurationPolicy||OPTIONAL, REQUIRE, IGNORE||Configuration Policy||
+||enabled||Boolean||Enabled component||
+||properties||String[]||Properties specified as an array of key=value. The property type can be specified in the key as name:Integer. The value can contain multiple values, the parts must then be separated by a vertical bar ('|') or a line feed (\n), for example properties = {"primes:Integer==1|2|3|5|7|11"}.||
||!Reference|| +On a method. Indicates this method is the activate method. It has the following attributes
+||name||String||Name of the reference. Default this is the name of the method without set on it.|| +||service||Class||The service type, default is the argument type of the method. The unset method is derived from this name. I.e. setXX will have an unsetXX method to unset the reference.|| +||type||Character||Standard cardinality type '?', '*', '+','~'|| +||target||String||A filter expression applied to the properties of the target service|| +||unbind||String||Optional name of the unbind method. By default this is the same name as the bind method name but with set/add replaced with unset/remove. E.g. setFoo() bind method becomes unsetFoo() unbind method.||
+@Reference automatically sets the bind method. The unbind method is set by using a derived name from the bind method or providing it with the name of the unbind method. The following name patterns are supported:
||!bind||!unbind||
+||setX ||unsetX||
+||addX ||removeX||
+||xxxX ||unxxxX||
+For example:
@Reference
+protected void setFoo(LogService l) { ... }
+protected void unsetFoo(LogService l) { ... }
+If you want to override this, use
+@Reference(unbind="IRefuseToCallMyMethodUnFoo");
+protected void foo(LogService l) {}
+protected void IRefuseToCallMyMethodUnFoo(LogService l) {}
+Unfortunately Java has no method references so it is not type safe.A non existent @UnReference annotation is not very useful because that still requires linking it up symbolically to the associated @Reference.
||!Activate, Modified, and Deactivate|| +The life cycle methods. These annotations have no properties.
+Assume the JAR contains the following class:
+package com.acme;
+import org.osgi.service.event.*;
+import org.osgi.service.log.*;
+import aQute.bnd.annotation.component.*;
+
+@Component
+public class AnnotatedComponent implements EventHandler {
+ LogService log;
+
+ @Reference
+ void setLog(LogService log) { this.log=log; }
+
+ public void handleEvent(Event event) {
+ log.log(LogService.LOG_INFO, event.getTopic());
+ }
+}
+The only thing necessary to register the Declarative Service component is to add the following Service-Component header:
+Service-Component: com.acme.*
+This header will look for annotations in all com.acme sub-packages for an annotated component. The resulting XML will look like:
+OSGI-INF/com.acme.AnnotatedComponent.xml:
+<?xml version='1.0' encoding='utf-8'?>
+<component name='com.acme.AnnotatedComponent'>
+ <implementation class='com.acme.AnnotatedComponent'/>
+ <service>
+ <provide interface='org.osgi.service.event.EventHandler'/>
+ </service>
+ <reference name='log'
+ interface='org.osgi.service.log.LogService'
+ bind='setLog'
+ unbind='unsetLog'/>
+</component>
+The following example shows a component that is bound to the log service via the setLog method without annotations:
+Service-Component=aQute.tutorial.component.World;
+ log=org.osgi.service.log.LogService
+The Service Component Runtime (SCR) offers a variety of options on the reference. Some options like the target can be used by adding the target filter after the interface name (this likely requires putting quotes around the interface name+filter).
+References can be suffixed with the following characters to indicate their cardinality:
+Char Cardinality Policy
+? 0..1 dynamic
+* 0..n dynamic
++ 1..n dynamic
+~ 0..1 static
+ 1 static
+For a more complex example:
+Service-Component=aQute.tutorial.component.World;
+ log=org.osgi.service.log.LogService?;
+ http=org.osgi.service.http.HttpService;
+ PROCESSORS="xierpa.service.processor.Processor(priority>1)+";
+ properties:="wazaabi=true"
+The previous example will result in the following service component in the resource OSGI-INF/aQute.tutorial.component.World.xml:
<?xml version="1.0" encoding="utf-8" ?>
+<component name="aQute.tutorial.component.World">
+ <implementation class="aQute.tutorial.component.World" />
+ <reference name="log"
+ interface="org.osgi.service.log.LogService"
+ cardinality="0..1"
+ bind="setLog"
+ unbind="unsetLog"
+ policy="dynamic" />
+ <reference name="http"
+ interface="org.osgi.service.http.HttpService"
+ bind="setHttp"
+ unbind="unsetHttp" />
+ <reference name="PROCESSORS"
+ interface="xierpa.service.processor.Processor"
+ cardinality="1..n"
+ policy="dynamic"
+ target="(&(priority>=1)(link=false))" />
+</component>
+The description also supports the immediate, enabled, factory, target, servicefactory, configuration-policy, activate, deactivate, and modified attributes. Refer to the Declarative Services definition for their semantics.
+If any feature of the V1.1 namespace is used, then bnd will declare the namespace in the component element. A specific namespace version can be set with the version directive. This detection only works when components are used.
Bnd also supports setting the policy and cardinality through the following directives:
+multiple:= LIST names of references that have x..n
+optional:= LIST names of references that have 0..x
+dynamic:= LIST names of references that are dynamic
+The Service Component Runtime works closely together with the Configuration Admin to allow the components to be controlled through configuration. Configuration Admin knows two types of configuration:
+A unitary configuration can be set and changed but there is at most one of them. A Factory configuration can be used to create multiple instances of the same component. A component has a configuration policy that defines when no configuration is set.
+A related standard is the Metatype standard. The Metatype Service provides a repository of property descriptors. Bundles can provide these descriptors in their bundles in the OSGI-INF/metatype directory. There are tools, like the Felix Webconsole, that can provide an editing window for a configuration that is typed with a metatype description.
+In practice, this is a powerful model that provides a lot of configurability for your components with easy editing but getting it all right is not trivial. To make this easier, bnd has made it ease to use configurations.
+In this model, configurations are declared in an interface. For example, the following interface defines a simple message:
+interface Config {
+ String message(); // message to give
+}
+To create a component that can work with this config, we need to designate that interface as the configuration interface for a component.
+@Component(designate=Config.class)
+public class BasicComponent {
+ Config config;
+
+ @Activate void activate(Map<String,Object> props) {
+ config = Configurable.createConfig(props);
+ System.out.println("Hi " + config.message());
+ }
+
+ @Deactivate void deactivate() {
+ System.out.println("Bye " + config.message());
+ }
+}
+This is an immediate component because it does not implement a service interface. It also requires a configuration because we have not specified this explicitly. When you use designate (or designateFactory) the default becomes require. This means that your component will only be created when there is actually configuration for it set.
+To run this component, make sure you have the Felix Webconsole running and the MetaType service installed. In the Webconsole, you can click on +'''Configuration''', your component should be listed on this page. By Clicking on the component with the name '''Basic Component Config''' you get an editor window.
+The editor is aware of the proper types, it uses the [[MetaType]] standard to describe the properties. bnd uses the type information on the interface as well as the optional Metadata annotations to create a rich description that allows the web console to provide a good editor.
+You can fill in the message in the ''Message'' field. If you save the editor, your component prints the message with "Hi" in front of it. Deleting the configuration will print the message with "Bye".
+If you change the message, you will see that the component is first deactivated and then reactivated again. This is the only possibility for the SCR because the component has not implemented a modified method. Adding the following method will change this, now changes to the configuration are signaled to the component and the component can continue to work. This is more complicated then recycling the component but it can create a more optimized system.
+@Modified
+void modified( Map<String,Object> props) {
+ // reuse activate method
+ activate(props);
+}
+It is also possible to take advantage of the configuration factories. In this model
+An example, that implements a simple socket server on a configurable port and returns a message when a telnet session is opened to that port can be found on Github.
+ + diff --git a/org.bndtools.help/docs/chapters/210-metatype.html b/org.bndtools.help/docs/chapters/210-metatype.html new file mode 100644 index 0000000000..bdac990024 --- /dev/null +++ b/org.bndtools.help/docs/chapters/210-metatype.html @@ -0,0 +1,219 @@ + + + + +The OSGi Metatype specification provides a language to describe configuration information in an XML file. However, XML is cumbersome to use and eXtreMeLy error prone, and refactoring often finds it hard to change references in XML text files. Especially with DS components managing the relations can be complex, see Components
+For this reason, bnd provides annotations to define the XML based on a ''configuration'' interface. For example, the following interface defines a simple metatype:
+ package com.acme;
+ import aQute.bnd.annotation.metatype.*;
+
+ @Meta.OCD interface Config {
+ int port();
+ }
+To turn this into an XML file, the bnd file must contain:
+ -metatype: *
+The wild card will search through the whole bundle but it is possible to limit this to a restricted set of packages to speed up the processing. For example:
+ -metatype: *Metatype*, com.libs.metatypes
+If the previous Config interface is present in the output, the output will also contain an XML file at OSGI-INF/metatype/com.acme.Config.xml. This file will look as follows:
<metatype:MetaData
+ xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.1.0'>
+ <OCD
+ name='Config'
+ id='aQute.metatype.samples.Config'
+ localization='aQute.metatype.samples.Config'>
+ <AD
+ name='Port'
+ id='port'
+ cardinality='0'
+ required='true'
+ type='Integer'/>
+ </OCD>
+ <Designate pid='aQute.metatype.samples.Config'>
+ <Object ocdref='aQute.metatype.samples.Config'/>
+ </Designate>
+ </metatype:MetaData>
+bnd leverages the rich type information the Java class files contain. It inspects the returns types and from this it automatically calculates the AD type as well as the cardinality. For example, a method like:
+ List<Integer> ints()
+Provides an AD of:
+ <AD
+ name='Ints'
+ id='ints'
+ cardinality='-2147483648'
+ required='true'
+ type='Integer'/>
+bnd attempts to make the names of the OCD and AD human readable by un-camel-casing the id. This means that it uses the upper cases in the id to decide where to use spaces. It also attempts to replace '_' characters with spaces and removes '$'. If the result is not what is wanted, the name can always be explicitly set with the AD.name() field.
+The id of the properties is by default derived from the method name. The name space of methods is restricted by the Java language both in character set as well as by the reserved keywords. Therefore, bnd mangles the name of the method to allow the method name to be adapted to common practices in proeprty names.
+$new maps to the new name. To make the name $x, use a method with the name $$x.a.b.c comes from a_b_c) and to indicate private properties, that is properties that start with a '.'. For example, _secret maps to .secret and this will be a property that is not registered as a service. For example:
+ @OCD
+ interface Config {
+ String _password(); // .password - will not be registered as a service
+ String $new(); // new - keyword
+ }
+The OCD annotation is necessary to know that an interface is a Metatype interface. It should be used preferably without any parameters (they all have good defaults). However, each default is possible to override. The following table discusses the fields:
+|name|String|A human readable name of the component, can be localized if it starts with a % sign. The default is a string derived from the id where _, $, or camel casing is used to provide spaces.|
+|id|String|The id of the OCD, this will also be used for the pid of the Designate element.|
+|localization|String|The localization id of the metatype. This refers to a properties file in the OSGI-INF/i10l/description|String|A human readable description that can be localized. Default is empty.|
+|factory|String|Will treat this OCD as intended to be for a factory configuration. The default is false|
The AD is an optional annotation on methods of a OCD interface. The annotation makes it possible to override the defaults and provide extra information.
|name|String|A human readable name of the attribute, can be localized if it starts with a % sign. The default is a string derived from the method name where , $, or camel casing is used to provide spaces.|
+|id|String|The id of the attribute. By default this is the name of the method|
+|description|String|A human readable description that can be localized. Default is empty.|
+|type|String|The type of the attribute. This must be one of the types defined in the Metatype specification. By default the type will be derived from the return type of the method. If no applicable type can be found then the String type is used as final default.|
+|cardinality|int|The cardinality of the attribute. If this is negative its absolute indicates maximum number of elements in a Vector. If it is positive it indicates the maximum number of values in an array. Zero indicates a scalar. If not provided, bnd will calculate the cardinality based on the return type. Collections will have a negative large value and arrays have a positive large value.|
+|min|String|The minimum value allowed for this attribute. There is no default.|
+|max|String|The maximum value allowed for this attribute. There is no default.|
+|deflt|String|The default initial value. The default for this is an empty String|
+|required|boolean|Indicates if this attribute is required. The default is that attributes are required.|
+|optionLabels|String[]|Labels for any values specified in optionValues. The default value is unset if there are no optionValues are defined. If these are defined, then the labels are calculated from the values by making , $ into spaces and providing spaces between camel cased words in the values.|
+|optionValues|String[]|Optional values. If this field is not set and the return type is an enum type then the values are calculated from the enum members.|
The type support the OSGi Metatype specification is limited to the primitives and strings. So what happens when you use another type? During the build phase, bnd will revert to a 'String' Metatype type. This means that those special types are going to be strings in the dictionary. However, the aQute.bnd.annotation.metatype package contains a helper class that simplifies the usage of properties in runtime.
+It has a @createConfigurable(Class<T> c, Map<?,?> props). This method returns an object that implements the given configuration interface. The implementation of these methods uses the properties to provide a value. That is, calling the method abc() on this interface will attempt to find the property "abc". It will then use the actual return type of the method to do conversion from the type in the properties to the return type. This takes generic information into account when present.
For example:
+ @Meta.OCD
+ interface Config {
+ URI[] uris();
+ }
+
+ public void updated( Map<String,Object> props) {
+ config = Configurable.createConfigurable(Config.class,props);
+ for ( URI URI : config.uris() ) {
+ ...
+ }
+ }
+The Metatype specification does not support URIs, so how does this work? Lets first look at the AD:
+ <AD
+ name='uris'
+ id='uris'
+ cardinality='2147483647'
+ required='true'
+ type='String'/>
+Any editor will therefore put an array of strings (String[]) in the properties. When the proxy gets the string, it will therefore have to convert from the String[] -> URI[]. In this case, the URI has a String constructor and is used to do the conversion from String to URI. The converter can handle general array to collection, collection to array, and as indicated, any conversion to an object that has a String constructor.
For convenience there are a number of built int conversions provided that cannot leverage the String constructor:
valueOf method to get an instance.The following example shows a very simple metatype configuration interface:
+ package aQute.metatype.samples;
+ import aQute.bnd.annotation.metatype.Meta.*;
+ @OCD
+ public interface Config {
+ int port();
+ }
+If the bnd.bnd file contains:
+ -metatype: *
+Then bnd will detect this class as a Metatype and it generates the following XML in `OSGi-INF/metatype/aQute.metatype.samples.Config.xml
+ <?xml version='1.0'?>
+ <metatype:MetaData
+ xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.1.0'>
+ <OCD
+ name='Config'
+ id='aQute.metatype.samples.Config'
+ localization='aQute.metatype.samples.Config'>
+ <AD
+ name='Port'
+ id='port'
+ cardinality='0'
+ required='true'
+ type='Integer'/>
+ </OCD>
+ <Designate pid='aQute.metatype.samples.Config'>
+ <Object ocdref='aQute.metatype.samples.Config'/>
+ </Designate>
+ </metatype:MetaData>
+As usual, XML does an outstanding job in obfuscating the interesting parts. If you're using the Apache Felix Webconsole (and if not, why not?) then you can edit this metatype on the web.
+This metatype can now be used in a simple example that prints the port number:
+ package aQute.metatype.samples;
+ import java.util.*;
+ import org.osgi.service.cm.*;
+ import aQute.bnd.annotation.component.*;
+ import aQute.bnd.annotation.metatype.*;
+
+ @Component(properties="service.pid=aQute.metatype.samples.Config")
+ public class Echo implements ManagedService {
+
+ public void updated(Dictionary properties)
+ throws ConfigurationException {
+ if ( properties != null ) {
+ Config config = Configurable.createConfigurable(
+ Config.class, properties);
+ System.out.println(config.port());
+ }
+ }
+ }
+The editor can get quite rich with the metatype information.
+This information came from the following Meta interface:
+ interface SampleConfig {
+ String _secret();
+ String $new();
+ String name();
+ enum X { A, B, C; }
+ X x();
+ int birthYear();
+ URI uri();
+ URI[] uris();
+ Collection<URI> curis();
+ Collection<Integer> ints(); // fails on webconsole
+ }
+Though this is a big savings over normal fudging with properties, it gets better. The metatyping is fully integrated with DS. In this example, we're using DS to register the Managed Service but this is not necessary because DS will automatically use the name of a component as the PID. So with a component life can be as easy as:
+ @Component(designate=Config.class)
+ public class Echo2 {
+ @Activate
+ void activate(Map<?,?> properties) throws ConfigurationException {
+ Config config = Configurable.createConfigurable(
+ Config.class, properties);
+ System.out.println(config.port());
+ }
+ }
+No more strings. Components and metatypes are extensively explained in Components.
+Also see the articles on Service Components and Debugging.
+ + diff --git a/org.bndtools.help/docs/chapters/220-contracts.html b/org.bndtools.help/docs/chapters/220-contracts.html new file mode 100644 index 0000000000..aa96c22749 --- /dev/null +++ b/org.bndtools.help/docs/chapters/220-contracts.html @@ -0,0 +1,69 @@ + + + + +The OSGi has a very elegant package version model there are still many that think this is too much work. They do not want to be bothered by the niceties of semantic versions and just want to use, let's say, Servlet 3.0. For those people (seemingly not interested in minimizing dependencies) the OSGi Alliance came up with Portable Java Contracts. A contract allows you to:
+This very common pattern is called the Capability/Requirement (C/R) model in OSGi, it underlies all of its dependency concepts like Import/Export package and others; it forms the foundation of the OSGi Bundle Repository. If you ever want to know what is happening deep down inside a framework than look at the Wiring API and you see the requirements and capabilities in their most fundamental form. +You can find more information about the Portable Contracts in this blogpost.
+Capabilities declare a set of properties that describe something that a bundle can provide. A Requirement in a bundle has a filter that must match a capability before this bundle can be resolved. To prevent requirements matching completely unrelated capabilities they must both be defined in the same namespace,, where the namespace then defines the semantics of the properties. Using the C/R model we were able to describe most of the OSGi dependencies with surprisingly few additional concepts. For a modern OSGi resolver there is very little difference between the Import-Package and Require-Bundle headers.
+So how do those contracts work? Well, the bundle that provides the API for the contract has a contract capability. What this means is that it provides a Provide-Capability clause in the osgi.contract namespace, for example:
+Bundle P:
+ Provide-Capability:
+ osgi.contract;
+ osgi.contract=JavaServlet;
+ uses:="javax.servlet,javax.servlet.http";
+ version:Version="3.0"
+ Export-Package: javax.servlet, javax.servlet.http
+This contract defines two properties, the contract name (by convention this is the namespace name as property key) and the version. A bundle that wants to rely on this API can add the following requirement to its manifest: +Bundle R:
+ Require-Capability: osgi.contract;
+ filter:="(&(osgi.contract=JavaServlet)(version=3.0))"
+ Import-Package: javax.servlet, javax.servlet.http
+Experienced OSGi users should have cringed at these versionless packages, cringing becomes a gut-reaction at the sight of versionless packages. However, in this case it actually cannot harm. The previous example will ensure that Bundle P will be the class loader for the Bundle R for packages javax.servlet, javax.servlet.http. The magic is in the uses: directive, if the Require-Capability in bundle R is resolved to the Provide-Capability in bundle P then bundle R must import these packages from bundle P.
+bnd has support for this. First bnd can make it easier to create the Provide Capability header since the involved packages are in the Export-Package as well as in the Provide-Capability headers. The do-no-repeat-yourself mantra dictated an ${exports} macro. The ${exports} macro is replaced by the exported packages of the bundle, for example:
Bundle P:
+ Provide-Capability: \
+ osgi.contract;\
+ osgi.contract=JavaServlet;\
+ uses:="${exports}";\
+ version:Version="3.0"
+ Export-Package: javax.servlet, javax.servlet.http
+Furthermore there is the -define-contract instruction which can be applied in order to define a contract which is not available on the build path.
+That said, the most extensive help you get from bnd is for requiring contracts. Providing a contract is not so cumbersome, after all you're the provider so you have all the knowledge and the interest in providing metadata. Consuming a contract is less interesting and it is much harder to get the metadata right. In a similar vein, bnd analyzes your classes to find out the dependencies to create the Import-Package statement, doing this by hand is really hard (as other OSGi developing environments can testify!).
So to activate the use of contracts, add the -contract instruction:
bnd.bnd:
+ -contract: *
+This instruction will give bnd permission to scan the build path for contracts, i.e. Provide-Capability clauses in the osgi.contract namespace. These declared contracts cause a corresponding requirement in the bundle when the bundle imports packages listed in the uses clause. In the example with Bundle R, bnd will automatically insert the Require-Capability header and remove any versions on the imported packages.
+Sometimes the wildcard for the -contract instruction can be used to limit the contracts that are considered. Sometimes you want a specific contract but not others. Other times you want to skip a specific contract. The following example skips the 'JavaServlet' contract:
+bnd.bnd:
-contract: !JavaServlet,*
+See -contract and -define-contract instruction for defining contracts in bnd.
+The ContractTest.java provides some examples for people that want to have a deeper understanding.
+Manifest headers are challenging to keep in sync with the code in the bundle. It often takes several attempts to get all the details correct.
+One of the goals of bnd is to eliminate such issues by relying on Java's type system to express the semantics of OSGi metadata.
+To address this bnd pioneered manifest annotations which evolved into OSGi's bundle annotations. A bundle annotation is used to express metadata that cannot otherwise be derived from code.
+A bundle annotation is applied to a type or package and when processed by bnd will cause the generation of corresponding manifest headers (and header clauses). Generating manifest headers from type safe structures is far less likely to result in errors, simplifies the developers life and is more conducive to code refactoring which won't result in information loss.
+The following example shows the preferred way to handle package versioning by applying the @Export and @Version bundle annotations to com/acme/package-info.java.
@Export
+@Version("1.3.4")
+package com.acme;
+which results in the manifest header:
+Export-Package: com.acme;version="1.3.4"
+Some developers do not want to rely on the additional dependency of bnd/OSGi annotations. For this reason, it is possible to also apply the annotations textually in comments to the files in the META-INF/services.
+This trick avoids a compile time dependency. These Service Loader files have the name of a Service Loader service and contain the names of the implementation classes. One fully qualified name per line.
To make these annotations in the comments more readable, it is possible to import the fully qualified name of the annotation.
+META-INF/services/com.example.ServiceType:
+
+ #import aQute.bnd.annotation.spi.ServiceProvider
+ #@ServiceProvider(resolution:=optional)
+ com.example.impl.Impl2
+ #@ServiceProvider(attribute:List<String>="a=1, b=2")
+ com.example.impl.Impl1
+The processing is identical to the normal class based annotation processing. The #class macro will be set to the implementation class. The #value will be set in all cases to the service type unless overridden.
This behavior can be controlled with the -metainf-services instruction. Default is annotation which processes the textual annotations above, while the other convenience strategy auto automatically creates Provide-Capability headers for services without any textual annotations.
While the above is a compromise, clearly using the real class annotation is far superior:
+The analysis of these files happens after the analyzer plugins have been run. These plugins can add files if so desired.
+Since the annotations & imports happen in the comments, it is not possible to diagnose any errors. If the comment does not match its regular expression, it will be silently ignored.
+Though Java class files contain enough information to find code dependencies, there are many dependencies that are indirect. OSGi extenders for instance are often a requirement to make a bundle function correctly but often client bundles have no code dependency on the extender. For example, Declarative Services (DS) went out of its way to allow components to be Plain Old Java Objects (POJO). The result is that resolving a closure of bundles starting from a DS client bundle would not drag in the Service Component Runtime (SCR), resulting in a satisfied but rather idle closure.
+The solution was to describe the requirement for the runtime SCR dependency using Requirements and Capabilities. But again, writing these complex clauses in the manifest by hand is both error prone and painful.
+The @Requirement and @Capability annotations were designed to address this issue. These annotations can be used to create custom bundle annotations, described later on. Let's discuss the DS example.
Recent DS specifications require implementations to provide the following capability:
+Provide-Capability: osgi.extender;
+ osgi.extender="osgi.component";
+ version:Version="1.4.0";
+ uses:="org.osgi.service.component"
+While this provides a capability that can be required, we need a requirement to be generated from client code that uses DS. Enter recent versions of DS annotations which are meta-annotated with @RequireServiceComponentRuntime, a custom bundle annotation which is specified as:
@Requirement(
+ namespace = ExtenderNamespace.EXTENDER_NAMESPACE,
+ name = ComponentConstants.COMPONENT_CAPABILITY_NAME,
+ version = ComponentConstants.COMPONENT_SPECIFICATION_VERSION)
+@Retention(RetentionPolicy.CLASS)
+public @interface RequireServiceComponentRuntime { }
+If you inspect the source code for @Component you'll find it is meta-annotated with @RequireServiceComponentRuntime. When you write a DS component using @Component as follows
@Component
+class Foo { ... }
+and because of the inherent bundle annotations it holds, the following manifest clause is generated
+Require-Capability: \
+ osgi.extender; \
+ filter:="(&(osgi.extender=osgi.component)(version>=1.4.0)(!(version>=2.0.0)))"
+The invisible link created between user code and the indirect requirement is a powerful mechanism that enables automatic validation of a bundle closure.
+The actual requirement filter: directive is constructed from an AND of the filter(), name(), and version() annotation methods. All fields are optional. The name field will create an assertion that the given namespace equals the value of the name() annotation method. For example, if the namespace is com.example.foo and the name() method has the value bar then the filter is (com.example.foo=bar). If a version is specified, it will be expanded to a filtered version-range expression. The convention of using the namespace name as the property key is commonly used in OSGi specification. For example, the filter (osgi.wiring.package=com.example.foo) is the filter for an Import-Package com.example.foo while osgi.wiring.package is the namespace for the packages.
For example:
+@Requirement(namespace = "NAMESPACE", name="NAME", version="1.2.3", filter="(foo=${#foo})")
+@Retention(RetentionPolicy.CLASS)
+public @interface RequireSomething {
+ int foo();
+}
+
+@RequireSomething(foo=3)
+class Foo {...}
+This will generate a manifest Require-Capability header of:
+Require-Capability: \
+ NAMESPACE; \
+ filter:="(&(foo=3)(NAMESPACE=NAME)(version>=1.2.3)(!(version>=2.0.0)))"
+Bundle annotations aren't just about package versioning or requirements and capabilities. They are about lifting metadata out of our code to avoid, among other things, error prone duplication of information. A common example is the bundle activator. Bundle Activators are require to be described in a manifest header. This association is not visible to refactoring tools and as such can easily end up out of sync.
+The @Header annotation exists to address this problem.
package com.acme;
+
+@Header(name = Constants.BUNDLE_ACTIVATOR, value = "${@class}")
+public Activator implements BundleActivator { ... }
+results in the manifest header:
+Bundle-Activator: com.acme.Activator
You'll note the string ${@class} used in the above example. String fields in bundle annotations are processed through bnd's macro processor. This macro processor provides access to all default and builder macros. More info on bnd macros can be found in the macros chapter.
Bnd also provides access to certain key properties of the current processing state.
+The @Header example above used the macro ${@class} which lifted the @class property holding the class name of the activator into the header to avoid having to duplicate it. This also means that refactoring the activator won't cause the manifest to get out of sync.
In the case that a bundle annotation is used as a meta annotation then the methods on the annotated annotation are available as macros as well with a name prefixed with #. That is, if the annotated annotation has a method foo(), then the macro ${#foo} can be used to refer to its value. See Accessor Properties for more details.
Certain bundle annotations have a second important use. We know that if applied to a type or package bundle annotations result in a clause in the manifest. However, many can be used as meta-annotations to a second annotation. The second annotation is considered a custom bundle annotation. The custom bundle annotation results in a manifest clause only when applied to a type or package.
+This makes it possible to create an annotation for a subsystem. For example, an annotation @ASL_2_0 that sets the Bundle-License header to the Apache Software License version 2.0.
@BundleLicense(
+ name = "https://www.opensource.org/licenses/apache2.0.php",
+ link = "https://www.apache.org/licenses/LICENSE-2.0.html",
+ description = "Apache Software License 2.0")
+@interface ASL_2_0 {}
+
+// takes effect when applied to a type
+
+@ASL_2_0
+class Foo { ... }
+When creating custom bundle annotations a common requirement is to make them parameterizable such that the values of the custom bundle annotation feed into the header clauses resulting from the bundle annotation applied to it (remember; a custom bundle annotation is meta-annotated with a bundle annotation**.)
+OSGi specifies two annotations, @Attribute and @Directive, for this purpose. Any methods of the custom bundle annotation annotated with @Attribute or @Directive will result in those becoming additional attributes or directives respectively of the resulting header clause when a value is supplied.
Note: When bnd processes OSGi headers, it automatically ensures consistent ordering of attributes and directives within header clauses, with attributes appearing before directives and both groups sorted alphabetically. See OSGi Header Attribute and Directive Ordering for more details.
+@Attribute@Attribute allows you to add new or update existing attributes from the bundle annotation.
@Capability(namespace = "foo.namespace")
+@interface Extended {
+ @Attribute("foo.attribute") // this attribute enhances the @Capability
+ String value();
+}
+
+// usage
+
+@Extended("bar")
+class Foo {}
+which results in the manifest header:
+Provide-Capability: foo.namespace;foo.attribute=bar
@Directive@Directive behaves similarly; with some caveats. You can add new or update existing directives for namespaces not defined by OSGi specifications.
@Capability(namespace = "foo.namespace")
+@interface Extended {
+ @Directive("foo.directive")
+ String value();
+}
+
+// usage
+
+@Extended("bar")
+class Foo {}
+results in the manifest header:
+Provide-Capability: foo.namespace;foo.directive:=bar
However, namespaces defined by OSGi specifications will be validated and will not accept directives which are not part of the spec unless they are prefixed with x-.
@Capability(namespace = "osgi.extender", name = "bar", version = "1.0.0")
+@interface Extended {
+ @Directive("foo")
+ String value();
+}
+
+// usage
+
+@Extended("bar")
+public class Foo {}
+will result in an error:
+Unknown directive 'foo:' for namespace 'osgi.extender' in 'Provide-Capability'. Allowed directives are [effective:,uses:], and 'x-*'.
It should be noted that it's possible to elide such errors using bnd's -fixupmessages instruction.
This next example however:
+@Capability(namespace = "osgi.extender", name = "bar", version = "1.0.0")
+@interface Extended {
+ @Directive("x-foo")
+ String value();
+}
+
+// usage
+
+@Extended("bar")
+public class Foo {}
+results in the manifest header:
+Provide-Capability: osgi.extender;osgi.extender=bar;version:Version="1.0.0";x-foo:=bar
It should be noted that default values for methods annotated with @Attribute and @Directive are deemed to be for documentation purposes only and will not be emitted into resulting headers.
For more customisation options see chapter on Accessor Properties.
+OSGi bundle annotations can be found in the osgi.annotation (e.g. org.osgi:osgi.annotation:7.0.0) bundle.
<dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.annotation</artifactId>
+ <version>7.0.0</version>
+</dependency>
+Bnd bundle annotations can be found in the biz.aQute.bnd.annotations (e.g. biz.aQute.bnd:biz.aQute.bnd.annotation:5.0.0) bundle.
<dependency>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>biz.aQute.bnd.annotation</artifactId>
+ <version>${bnd.version}</version>
+</dependency>
+OSGi Bundle Annotations:
+@Attribute@Capability@Directive@Export@Header@Requirement@RequireConfigurationAdmin@RequireMetaTypeExtender@RequireMetaTypeImplementation@RequireServiceComponentRuntime@RequireEventAdmin@RequireJPAExtender@RequireHttpWhiteboard@RequireConfigurator@RequireJaxrsWhiteboard@JSONRequired@RequireCDIExtender@RequireCDIImplementationBnd Bundle Annotations:
+@BundleCategory – Sets the bundle category, existing categories are defined in an enum.@BundleContributors – Creates an OSGi header for contributors that maps to the Maven contributors element.@BundleCopyright – Sets the copyright header.@BundleDevelopers – Creates an OSGi header for developers that maps to the Maven developers element.@BundleDocUrl – Provides a documentation URL.@BundleLicense - Creates entries in the Bundle-License header.@ASL_2_0@BSD_2_Clause@BSD_3_Clause@CDDL_1_0@CPL_1_0@EPL_1_0@GPL_2_0@GPL_3_0@LGPL_2_1@MIT_1_0@MPL_2_0
@ServiceConsumer - Generates requirements in support of the consumer side of the Service Loader Mediator specification.
@ServiceProvider - Generates requirements and capabilities in support of the provider side of the Service Loader Mediator specification. Also generates META-INF/service descriptors.When working with custom bundle annotations one may find that @Attribute and @Directive have limitations in scenarios where multiple bundle annotations are added to a single custom bundle annotation because there are no discriminators expressing which bundle annotation to associate the attribute or directive; and it's likely that associating these with all bundle annotations produces unexpected or even incorrect results. Bnd provides a solution which takes advantage of its macro support during annotation processing.
When an annotation is meta-annotated with a bundle annotation the values and defaults of the host are loaded as properties, prefixed with #, into the macro processing scope. The effect is that these values are accessible using a macro pattern (like ${}).
@Capability(
+ name = "${#name}", // <-- accesses the hosts 'name()' value
+ namespace = "osgi.cdi.extension",
+ version = "${#version}" // <-- accesses the hosts 'version()' value
+)
+@Requirement(
+ name = "osgi.cdi",
+ namespace = "osgi.implementation",
+ version = "1.0.0")
+@interface CDIExtension {
+ String name();
+ String version();
+}
+Annotation methods may return several different types and they are converted to strings according to the following table:
+| Return type | +Conversion | +
|---|---|
boolean, byte, char, double, float, int, long, short |
+String.valueOf() |
+
java.lang.Class |
+Class.getName() |
+
java.lang.Enum |
+Enum.name() |
+
java.lang.String |
+as is (commas should be escaped with \) |
+
| array type whose component type is one of the preceding types | +joined by comma (,) |
+
type satisfying Class.isAnnotation() |
+omitted from available accessor properties | +
array type whose component type satisfies Class.isAnnotation() |
+omitted from available accessor properties | +
It should be noted that if the resulting strings contain macro characters these will be interpreted by bnd's macro engine. This may produce unexpected results due to potentially nested macros. In this scenario steps must be taken to either escape these characters or that nesting is done to allow bnd to successfully process the result.
+The return type Class<?> is particularly useful when it comes to making your code friendly to refactoring.
@Capability(
+ filter = "objectClass:List<String>='${#value}'",
+ namespace = "osgi.service",
+)
+@interface MyService {
+ Class<?>[] value();
+}
+
+// applied as
+
+@MyService(Bar.class)
+class Bar { ... }
+What if you want the default to be the annotated class itself? Bnd made this possible by using a type which was unlikely to be a real argument. The type selected was java.lang.annotation.Targetbecause it is guaranteed to be accessible, is unlikely to be a real argument, will not require an additional dependency, and it's nomenclature proved the most expressive for the use case.
@Capability(
+ filter = "objectClass:List<String>='${#value}'",
+ namespace = "osgi.service",
+)
+@interface MyService {
+ Class<?>[] value() default Target.class;
+}
+
+// applied as
+
+@MyService
+class Bar { ... }
+Instances of java.lang.annotation.Target.class found in the return value of Class<?> or Class<?>[] methods will be replaced by the annotated type prior to string conversion.
In order to control the cardinality of requirements generated by custom bundle annotations a convenience enum aQute.bnd.annotation.Cardinality is available as a method return type. The default value should be Cardinality.DEFAULT ensuring that if not set no directive will be emitted.
@Requirement(
+ attribute = {
+ aQute.bnd.annotation.Constants.CARDINALITY_MACRO
+ },
+ namespace = "osgi.service",
+)
+@interface RequireMyService {
+ Cardinality cardinality() default Cardinality.DEFAULT;
+}
+
+// applied as
+
+@RequireMyService(cardinality = Cardinality.MULTIPLE)
+class Bar { ... }
+Note: It is recommended to use the macro constant aQute.bnd.annotation.Constants.CARDINALITY_MACRO crafted specifically for handling the cardinality directive. It should also be noted that during macro processing the enum name() will be converted to lower case.
In order to control the resolution of requirements generated by custom bundle annotations a convenience enum aQute.bnd.annotation.Resolution is available as method return type. The default value should be Resolution.DEFAULT ensuring that if not set no directive will be emitted.
@Requirement(
+ attribute = {
+ aQute.bnd.annotation.Constants.RESOLUTION_MACRO
+ },
+ namespace = "osgi.service",
+)
+@interface RequireMyService {
+ Resolution resolution() default Resolution.DEFAULT;
+}
+
+// applied as
+
+@RequireMyService(resolution = Resolution.OPTIONAL)
+class Bar { ... }
+Note: It is recommended to use the macro constant aQute.bnd.annotation.Constants.RESOLUTION_MACRO crafted specifically for handling the resolution directive. It should also be noted that during macro processing the enum name() will be converted to lower case.
In order to control the effective time of a requirement generated by custom bundle annotations a method called effective with a return type of String can be used in conjunction with the macro constant aQute.bnd.annotation.Constants.EFFECTIVE_MACRO. The default value should be the empty string ("") ensuring that if not set no directive will be emitted.
@Requirement(
+ attribute = {
+ aQute.bnd.annotation.Constants.EFFECTIVE_MACRO
+ },
+ namespace = "osgi.service",
+)
+@interface RequireMyService {
+ String effective() default "";
+}
+
+// applied as
+
+@RequireMyService(effective = "active")
+class Bar { ... }
+In order to control the uses constraints of a requirement generated by custom bundle annotations a method called uses with a return type of Class<?>[] can be used in conjunction with the macro constant aQute.bnd.annotation.Constants.USES_MACRO. The default value should be an empty array ({}) ensuring that if not set the directive will not be emitted.
@Requirement(
+ attribute = {
+ aQute.bnd.annotation.Constants.USES_MACRO
+ },
+ namespace = "osgi.service",
+)
+@interface RequireMyService {
+ Class<?>[] uses() default {};
+}
+
+// applied as
+
+@RequireMyService(uses = {com.acme.Foo.class, org.bar.Bar.class})
+class Bar { ... }
+Writing Java libraries which support OSGi does not typically require more than generating proper OSGi metadata. bnd helps accomplish with minimal effort on the part of developers. One issue that remains somewhat complex is properly handling the use of the Java SPI such that it works seamlessly in OSGi and avoiding the need for custom code to accomplish a similar goal. OSGi defines the Service Loader Mediator Specification but applying it requires a significant amount of manifest metadata.
+bnd defines a set of SPI annotations which provide a simple solution for developers who wish to maintain OSGi friendly libraries.
+The Java SPI use cases are broken into two groups; providers and consumers. The @aQute.bnd.annotation.spi.ServiceConsumer annotation is used in consumer code to express a requirement on an SPI service type adding the appropriate OSGi metadata to the manifest.
@ServiceConsumer(
+ value = JsonProvider.class
+)
+public abstract class JsonProvider {
+ public static JsonProvider provider() {
+ for (JsonProvider provider : ServiceLoader.load(JsonProvider.class)) {
+ return provider;
+ }
+ throw new JsonException("provider not found");
+ }
+ ...
+}
+@ServiceConsumer also grants the developer control in defining many facets of the generated OSGi requirements such as cardinality, effective time and resolution.
The provider side is supported by the @aQute.bnd.annotation.spi.ServiceProvider annotation. It is used to express a capability for a given SPI service type adding all the appropriate OSGi manifest metadata.
@ServiceProvider(JsonProvider.class)
+public class JsonProviderImpl extends JsonProvider {
+ ...
+}
+@ServiceProvider also grants the developer control in defining many facets of the generated OSGi requirements and capabilities such as additional attributes and directives (attributes becoming service properties), cardinality, effective time, resolution, and package uses.
The @ServiceProvider annotation will automatically result in publishing OSGi services for each provider discovered. Any attributes (excluding directives) specified on the annotation will be automatically added as OSGi service properties.
@ServiceProvider(
+ value = JsonProvider.class,
+ attribute = {
+ "colors:List<String>='blue,green,red'"
+ }
+)
+public class JsonProviderImpl extends JsonProvider {
+ ...
+}
+An osgi.service capability is also generated for each provider type.
An additional feature provided by bnd is the ability to manage the service descriptor files (a.k.a. META-INF/services/*) automatically for any osgi.serviceloader capabilities it finds having the register: directive containing a provider's fully qualified class name. The register: directive is automatically generated from all instances of @ServiceProvider.
This Application Note is about resolving in OSGi. The OSGi Framework has always used a resolver to wire a given set of bundles together, ensuring that only valid wires are made. However, the same OSGi resolver can also be used to select a set of bundles from a much larger set. This application note discusses this secondary usage.
+The resolver model is based on technology developed in OSGi since 2006 with RFC-0112 Bundle Repository. This RFC laid out a model that was gradually implemented in the OSGi specifications and for which many tools were developed. Resolving automates a task that is mostly done manually today.
+The pain that the resolver solves is well known to anybody that builds application from modules, the so called assembly process. All developers know the dreadful feeling of having to drag in more and more dependencies to get rid of Class Not Found errors when the application starts (or after running for an hour). For many, Maven was a significant improvement because it made this process so much easier because dependencies in Maven are transitive. If you depend on some artefact, then this artefact drags in its own dependencies to the runtime.
+Unfortunately, the Maven dependency model has limitations for a number of reasons. Though it undeniably has become easier to get a result, the result is often littered with unnecessary or wrong artefacts. Often application developers have no real understanding what is in their runtime.
+These are the causes:
+The result is that all too often the class path of the final application is littered with never used byte codes and/or overlapping packages that came from artefacts with different versions. If the customer could see the mess the average class path is they would run away screaming.
+Establishing the perfect class path for an application is a process for which the human mind is extremely badly suited and the transitive model of Maven has too many sharp edges. The resolver provides an alternative model that focuses on looking at the whole solution space instead of having to live with the (sometimes arbitrary) decisions of developers or of deeply nested transitive dependencies.
+
The design of the resolving model is quite simple. It consists of the following entities:
+String, Integer, Double, and Long.Resolving in this App note is therefore the process of constructing an application out of resources. Resolving takes a list of initial requirements, a description of the system capabilities, and one or more repositories. It will use the list of initial requirements to find resources in the repository that provide the required capabilities. Clearly, these resources have their own requirements, retrieving applicable resources is therefore a recursive process. A resolver will find either a solution consisting of a set of resources where all requirements are satisfied or that there is no solution.
+It is important to realise that a resource and its capabilities and requirements are descriptions. They provide a formal representation of an external artefact. Since these formal representations can be read by a computer, we can calculate a closure of resources that, when installed together, have only resources where all their mandatory requirements are satisfied by the resources in the closure.
+Although the OSGi specifications started out with a set of headers that each had their own semantics, over time the specification migrated fully to the more simple and formal model of Resources, Capabilities, and Requirements. Since the function of the legacy headers were still needed, it was necessary to map these legacy headers to the formal model. This resulted in a number of OSGi core namespaces.
+osgi.wiring.identity – Bundle-SymbolicName header.osgi.wiring.bundle – Bundle-SymbolicName and Require-Bundle header.osgi.wiring.package – Import-Package and Export-Package headers.osgi.wiring.host – Bundle-SymbolicName and Fragment-Host header.osgi.ee – Bundle-RequiredExecutionEnvironment header.osgi.native – Bundle-NativeCode header.osgi.content – Provides the URL and checksum to download the corresponding artefact. (This namespace is defined in the compendium Repository specification.)Each namespace defines the names of the properties and their semantics. For the OSGi namespaces, there are classes like org.osgi.framework.namespace.IdentityNamespace that contain the details of a namespace.
To make the model more clear let's take a closer look to a simple bundle com.example.bundle that exports package com.example.pe and imports a package com.example.pi. When the bundle is installed it will require that some other bundle, or the framework, provides package com.example.pi. We can describe this bundle then as follows:
Resource for bundle com.example.bundle
+ capabilities:
+ osgi.wiring.identity; osgi.wiring.identity=com.example.bundle
+ osgi.wiring.bundle; osgi.wiring.bundle=com.example.bundle
+ osgi.wiring.host; osgi.wiring.host=com.example.bundle
+ osgi.wiring.package; osgi.wiring.package=com.example.pe
+ requirements:
+ osgi.ee; filter:="(&(osgi.ee=JavaSE)(version=1.8))"
+ osgi.wiring.package; filter:='(osgi.wiring.package=com.example.pi)'
+Clearly, an Export-Package: com.example.pe is a bit easier to read than the corresponding capability, let alone the filter in the requirement. This is especially true when the versions are taken into account, with the assertion of the version range the filters become quite unreadable. Clearly, if this had to be maintained by human developers then a model like Maven would be far superior. Fortunately, almost all of the metadata that is needed to make this work does not require much extra work from the developer. For example, the bnd tool that is available in Maven, Gradle, Eclipse, Intellij and others can easily analyse a JAR file and automatically generate the requirements and capabilities based on the source code.
In certain cases it is necessary to provide requirements and capabilities that bnd cannot infer. However, Manifest annotations support in bnd can be used to define an annotation that will add parameterised requirements to the resource.
+Really, when you use an adequately appointed toolchain (like Bndtools) none of this gibberish is visible to normal developers unless they have a special case. The gibberish is left to the tools that prefer gibberish over natural language.
+For a bundle to be a good citizen in a resolution it suffices to follow the standard rules of good software engineering. However, since there is so much software out there that does not follow these rules, a short summary.
+java.* which is an implicit import). Therefore, a developer must always be aware of this trade off.To see the requirements and capabilities of a bundle Bndtools has a special Resolution view. This shows the requirements and capabilities of a selected JAR file or a bnd.bnd file.

This view uses a number of icons to represent the requirements/capabilities. You can hover over them to see further details.
+
– osgi.service.
– osgi.identity
– osgi.wiring.package
– osgi.ee
– osgi.extender
– osgi.bundleDevelopers that are keen on developing good bundles should pay close attention to this pane.
+
The previous diagram adds the resolver to the earlier diagram of the basic model. It adds the following entities:
+An important variable in the resolving process is effective which defines the effectiveness under which the resolve operation is performed. The resolver will only look at requirements that it deems effective. The default effectiveness is resolve. The effectiveness active is a convention commonly used for situations that do not need to be resolved by the OSGi framework but are relevant in using the resolver for assembling applications.
A repository is a collection of resources. This could be Maven Central or it could be a single bundle. That said, neither is a good idea in practice. The repository is the scope of the resolution. Only resources in the repository can be selected. Although the naive idea is to make the scope as large as possible to let the computer do the work, it is much better to curate the repository.
+There are many reasons why you need a curated repository but basically it comes down is the GIGO principle: garbage is garbage out. Another reason is running time. The resolver gets overwhelmed quickly when there are too many alternatives for a requirement. Resolving is an NP complete problem and this means that the resolution time quickly becomes very long when a lot of alternatives need to be examined.
+The bnd toolchain provides OSGi Repository access for many popular repository formats.
+pom.xml. It integrates fully with the local ~/.m2 repository.Since bnd has an extensive library to parse bundles and generate the resources it is relatively easy to parse bundles in other ways. For example, there is a Maven plugin that can generate an OSGi index.
+A repository generally represents a release of a software product. Many developers publish an OSGi Repository XML to allow the resolver access to the metadata of their bundles, for example Knopflerfish produces an OSGi XML Repository with all their bundles.
+This model is in contrast with Maven Central and most other Maven repositories. These repositories are designed to contain everything that was ever released. An OSGi repository is more the content of a specific release. Since it generally only contains a specific release, it will disallow the resolver to use any unwanted resources.
+A natural repository is the bnd workspace since a workspace is a collection of related projects. Since these projects are build together it is trivial to keep them compatible. The metadata burden can be mitigated by sharing metadata between these related projects. For example, in bnd all bundles share the same version. Although this might sometimes release unchanged bundles under a newer version, the only cost is a bit of disk space. A small price to pay for an otherwise very error prone manual process. The Gradle build can release to a Nexus repository and automatically generate an index that can be used as repository.
+By far the best way to get experience with the resolver is using the bnd's bndrun files. Bndtools provides a friendly user interface that makes it easy to use the OSGi Resolver in an interactive way.
A bndrun describes the runtime configuration for an application. This can either be a standalone executable JAR or an application that should be hosted in a container like a Java EE server or Karaf. In this app note, the target is an executable JAR unless otherwise noted.
If you double click on the bndrun file, it opens a Run pane.
+
>
Even though most lists are hidden, the pane is already quite overwhelming. So let's go through the GUI and explain it one by one.
+Remember that a resolution is for a specific system, in our case an executable JAR. In the Core Runtime pane we specify the OSGi framework that will be used as well as the execution environment. The execution environment is from a list of Java VM versions and OSGi specifications.

The execution environment is used to calculate the system capabilities. That is, the system is treated as a single resource that provides the capabilities of the Framework as well as the capabilities of the OSGi defined execution environments. The system resource is always included in the resolution but can of course never be downloaded, it is the target environment.
+The Browse Repos is a list of the resources that are found in the active repositories. A search field makes it easy to find specific resources. For example, if you type in gogo it will list all Gogo bundles.

One or more resources from the Browse Repos list can be selected and then dragged to the Run Requirements list to the left. This adds an identity requirement to the set of initial requirements. You can also drag a resource to the Run Blacklist and Run Bundles lists.
This is the main list to watch. It contains the set of initial requirements given to the resolver. The GUI makes it possible to add identity requirements and remove listed requirements. The easiest way is to use drag and drop form the Browse Repos list but it is also possible to use the green +, which opens a dialog from which bundles can be added directly from the selected repositories.

There are some more panes that are useful but they will be handled in diagnosing problems. For reference, a short introduction to these panes.
+bndrun file to standalone. A standalone bndrun file has no relation to the workspace it resides in and establishes its own repositories.Run, Debug, and Export functions always work from the -runbundles list.Taking the initial requirement it is possible to resolve by clicking on the Resolve button. This will show a rather large dialog window with the resolution.

This dialog window is divided in three main parts:
+If some of the listed optional resources are desired then they can be selected. Pressing the Update and Resolve button will restart the resolver but now with the selected optional resources as mandatory.
If the Finish button is pressed then the current required resources list is converted to the list of Run Bundles. You can inspect them at the right bottom of the bndrun editor window.

After a successful resolve you can either Run, Debug, or Export the bndrun file.
So far the ideal process of happy resolves and satisfied bundles has been described. It is now necessary to leave this rosy world and descend into the world of failed resolves. Unfortunately, the provided diagnostic information when a resolve fails is quite low.
+When a resolve fails it returns a cause but more often than not this is not the real cause. This is not some shortcoming from the current resolver but a fundamental logical problem. The simplest form of a resolution is if you have for example 3 numbers 1,4,8. You need to find the numbers that sum to 10 using only addition and subtraction. If you try out all the combination then you find that no combination works. A failure report could be that -3 is not available because the last tried permutation was 1+4+8. Clearly, before that permutation many other numbers were missing as well, the missing -3 just happened to be the last one ...
+That said, there are a number of scenarios where the resolver does give a hint where the problem is.
+This rather obscure message indicates that the resolver tries to include a api bundle that was made unresolvable.
+The api bundle may have a special Require-Capability header such as:
+ Require-Capability: \
+ compile-only
+This header creates a requirement that cannot be satisfied. There is nothing special with compile-only, it is just an unused namespace. It could also have been foo-bar.
In the resolver, you will see the following error chain:
+ Unable to resolve <<INITIAL>> version=null:
+ missing requirement osgi.enroute.examples.resolver.missingapi.provider
+ -> Unable to resolve osgi.enroute.examples.resolver.missingapi.provider version=1.0.0.201710041250:
+ missing requirement osgi.enroute.examples.resolver.missingapi.api; version=[1.0.0,1.1.0)
+ -> Unable to resolve osgi.enroute.examples.resolver.missingapi.api version=1.0.0.201710041249:
+ missing requirement false]]
+Note: Unfortunately, the output is blurred by a misguided attempt to make the output more concise. Because of this, the distinction between a bundle and a package is not very clear. Sadly you can only see the difference between a requirement for a bundle and a package by looking at the version. If this is a range then it is a package and if it is a version with a timestamp it is a bundle. (This works most of the time.) This is a bug in bnd and should be corrected.
+As indicated, you really need to understand that the diagnostic is just the last path the resolver took. The osgi.enroute.examples.resolver.missingapi.provider bundle tries to find a provider for the package osgi.enroute.examples.resolver.missingapi.api and has found the osgi.enroute.examples.resolver.missingapi.api bundle. However, this API bundle has the impossible to satisfy compile-only requirement.
That said, it is better to look at the Missing Requirements list since this reports quite nicely what is missing.

The icon and the text more clearly indicate that it cannot resolve the osgi.enroute.examples.resolver.missingapi.api bundle due to the compile-only requirement.
Exporting the API package from the provider bundle will correct this case.
+Many developers compile against the compendium bundle to get the OSGi service API packages. Although compiling against an API bundle has advantages, using the compendium bundle in runtime is evil. Since the compendium bundle aggregates a large number of API packages it will have the tendency to unnecessarily constrain the versions of different APIs. That is, it blocks you from using newer APIs.
+To make bundles that should not be used at runtime not resolvable there is the Run Blacklist list on the bndrun editor. This list contains bundles that should never be included in a resolution.
For example, we create a bundle that implements the Wire Admin service.
+ public class MyService implements WireAdmin {
+ ...
+ }
+This will cause a requirement for an exported package org.osgi.service.wireadmin. We therefore add the OSGi compendium bundle to the -buildpath. This then compiles fine.
However, the resolve will then drag in the OSGi compendium bundle.
+
If we look at the reasons when we select the compendium bundle we see that also the Configuration Admin imports from the compendium even though it actually might provide a higher version. (This maybe understandable but a really bad practice.)
+
To get rid of the compendium bundle we can drag and drop it to the Run Blacklist window. Any requirement in the Run Blacklist list will automatically exclude all bundles that are selected by that requirement.
Sometimes the resolver can complain about a missing requirement but you are sure that it is in the repository. The first thing is to try to isolate the problem. Almost any problem can be solved if you remove the redundant parts. Quite often developers are trying to debug this situation in a complex large bndrun file and then get overwhelmed.
Just create a new bndrun file and only add the bundle you think should provide the resource to the Run Requires list, keep only one requirement, and then resolve. If this resolves fine then at least you know that that bundle can potentially resolve.
However, often you find that even on its own it does not resolve. In most case the error message and the Reasons list provide sufficient information to understand why it does not resolve.
It is still a mystery, try checking the Run Blacklist list. If it is not there, it might be time to raise a bug.
Another tool for diagnosing potential issues in your OSGI framework (bundle, packages, services), the SCR info, configuration, log, and custom extensions is the OSGi bnd Snapshot Viewer.
+So far this App Note only visited the graphic user interface (GUI). However, bnd always keeps all information in simple properties files that can also edited as text. In the Run editor (that edits bndrun files) you can also select the Source view. Not all features of a bndrun file can be manipulated through the GUI. This section therefore shows what is in the source and it can be manipulated.
Notice that most instructions are merge properties. That is, bnd will first find all properties that start with the instruction name and merge their values together. For example, if you set -runrequires, -runrequires.foo, and -runrequires.bar bnd will use the combination of these properties. The order is the sorting order of the names used.
-distro option, it contains additional capabilities that are not in the distro files.The problem with metadata is that it can also be wrong. Especially legacy bundles lack the proper metadata to inform the resolver that they provide a service or require an implementation of a specification. This is not a problem in runtime since these requirements are generally not used by the Framework, they are designed for using the resolver in selecting a closure of bundles. However, in certain cases adding a capability or a requirement to a bundle in the repository would simplify things. Clearly it is possible to wrap the legacy bundle but this is extremely cumbersome and can create confusion down the line.
+Therefore, another method is to use the augments that the bnd repositories support. Augments can add capabilities and requirements to existing bundles in a repository. However, it can only do this for the interactive resolve process. When the OSGi Framework resolves a number of bundles it will never takes augments into account.
+There are two different ways to add the augments.
+bndrun file. This is an ad-hoc mechanism that is normally a last resort. For non-standalone bndrun files you can also add augments in the cnf/ext directory since any bnd file in there will add its properties to the bndrun file. It is of course also possible to include file in any bnd/bndrun file.bnd.augment capability. Such a resource has a file with properties that describe the augment for that repository. This is a good way when you have to curate a repository and need to fixup some legacy bundles. This resource can also add to the blacklist.In both cases the augments are described using the standard OSGi/bnd syntax. The syntax is not a beauty since it stretches what you can do with the OSGi header format. However, augmenting should really be a last resort so maybe it is not really bad that the syntax is cumbersome.
+You can add an augment with the -augment instruction in the bndrun file.
-augment PARAMETER ( ',' PARAMETER ) *
+Augmenting is adding additional capabilities and requirements. When bnd resolves a project or bndrun file, it will read these instructions. Since -augment is a merge property you can also use additional keys like -augment.xyz.
The key of the PARAMETER is used for matching the Bundle Symbolic Name. It can contain the * wildcard character to match multiple bundles. The bundle symbolic name must be allowed as a value in a filter it is therefore not a globbing expression.
The following directives and attribute are architected:
+[<version>,∞). The version range can be prefixed with an @ for a consumer range (to the next major) or a provider range (to the next minor) when the @ is a suffix of the version. The range can restrict the augmentation to a limited set of bundles.capability: directive specifies a Provide-Capability instruction, this will therefore likely have to be quoted to not confuse bnd with embedded comma's. Any number of clauses can be specified in a capability directive by separating the clauses with a comma. (This is where the syntax gets stretched.)requirement: directive specifies a Require-Capability instruction similar to the capability: directive.To augment the repositories during a resolve, bnd will find all bundles that match the bundle symbolic name and fall within the defined range. If no range is given, only the bundle symbolic name will constrain the search. Each found bundle will then be decorated with the capabilities and requirements defined in the capability: and requirement: directive.
For example, we need to provide an extender capability to a bundle with the bundle symbolic name com.example.prime with version [1.2,1.3). In that case add the following instruction to the bndrun file.
-augment.prime = \
+ com.example.prime; \
+ capability:='osgi.extender; \
+ osgi.extender=some.extender; \
+ version:Version=1.2@'
+The capability: and requirement: directives follow all the rules of the Provide-Capability and Require-Capability headers respectively. For the resolver, it is as if these headers were specified in their manifests. Since these headers can contain semicolons and commas they must be quoted. bnd will allow double quotes inside normal quotes and vice versa when it is necessary to nest quotes.
A workspace setup with bnd will generally provide a good start. However, when you need to grandfather in a lot of bundles from Maven Central then it is likely that you will need to spend some time to augment these bundles. This can be a depressing task since you'll find out how messy the world is. However, experience shows that once the repository is resolvable, maintaining it has very little overhead. Better, it tends to signal probems very early in the development process.
+There are a number of (rudimentary) functions in the command line version of bnd that might be useful. Unfortunately, the commands currently assume an OSGi repository.
+With openliberty, WebSphere Liberty, Karaf, Liferay, etc. you are deploying into an existing container which already has a lot of capabilities. The crux of the issue becomes resolving only what you need to deploy. What you need at that point is a way to find out what the container already provides in a format which can be used during resolve time.
+Currently, the way to do that is to create a distro of the target container. This distro is a JAR file which provides all the capabilities that the target container provides at one point in time. It includes the capabilities of all currently installed bundles. It also contains all capabilities provided by the system bundle which may have been configured by framework properties. It is an aggregate view of all the capabilities available in the framework contained in a single JAR.
+A Multi-Release JAR (MRJ) has directories in META-INF/versions/<release> (see JAR File Specification). When the JAR is deployed on a VM with a given release R, that VM will preferentially load file resource from its own release R and then down to the main area. This means that a JAR can have different content depending on the VM it is deployed. For OSGi, this means that a bundle resource can have different requirements based on the runtime VM. The capabilities are the same since the public API must not change.
The problem is that this makes some requirements dependent on the VM. To model this, we introduced synthetic resource for each supported VM release that can only resolve on that release and when the release specific requirements of the bundle can be resolved.
+This is only about modeling a JAR as a resource in an OSGi repository. Nothing is changed in the original JAR.
+So we first treat the multi release bundle as any other bundle when we turn it into a resource, the release directories are ignored. If there are none, we're actually done.
+Otherwise, we add a single requirement to a bnd.multirelease capability unique for this bundle. This namespace only has the properties bnd.multirelease=<bsn> and version=<version>. By requiring another resource, we can generate a synthetic resource for each VM release that the bundle supports. This is depicted here, where mrj is bnd.multirelease.
For the osgi.identity capability, we have to create a resource name that is unique. To keep it readable, the name is the original-bsn + "__" + release. The version is the original bundle's version. We introduced a new type for these synthetic resources: bnd.synthetic.
A Bundle x.y.z, providing code for JDK 1.8, 9 and 11 will appear as x.y.z__8, x.y.z__9 and x.y.z__11 with the corresponding narrow version ranges for their respective Java version.
We also add the requirements of the bundle if it would run on that release. +The synthetic resources are added to the same repository of the original bundle
+For example the bundle org.assertj:assertj-core:3.24.1 contains classes for Java 8 and Java 9. In the Resolution View of bndtools it is shown with the following two capabilities for each Java version:
FROM: assertj-core__8 version=3.24.1 type=bnd.synthetic
+bnd.multireleaseCapability from a supporting resource 0 part of Optional[assertj-core version=3.24.1]
+;
+ bnd.multirelease = assertj-core;
+ version = 3.24.1
+FROM: assertj-core__9 version=3.24.1 type=bnd.synthetic
+bnd.multireleaseCapability from a supporting resource 1 part of Optional[assertj-core version=3.24.1]
+;
+ bnd.multirelease = assertj-core;
+ version = 3.24.1
+Execute the following command using the bnd cli:
+bnd remote distro -o container-5.6.7.jar container 5.6.7
+Take the jar container-5.6.7.jar created in 2. and place it into the directory containing the bndrun file that is used to resolve your deployment jars.
in the bndrun file add:
+-distro: ${.}/container-5.6.7.jar;version=file
+resolve... the result of the resolve should be the set of bundles you need to install in the container, minus everything the container already provides.
+What you do with those resolved bundles is dependent on the goal of the developer. They can be directly installed in the existing container, or they could be assembled into a package format native to the container, or possibly a subsystem.
+What you need to bear in mind is that the distro needs to be re-created each time the target container changes in any significant way, otherwise it won't reflect the true capabilities of the system needed to resolve against.
+Creating applications from reusable models is a goal that the software industry has been trying to achieve for a long time. To a certain extent, the Maven dependency model provides this model. However, it also is a model where dependencies are not strictly managed and much is left to chance.
+The resolver model provides an alternative (working inside maven if so desired) to establish a class path that optimises the whole application class path instead of just slavishly following transitively dependencies. Experience shows that organisations that use the resolver have a much better grip of what is actually running in their runtime. Not only minimises this runtime errors, it generally also makes it easier to migrate and evolve the code base.
+Converting an existing build into a resolve based build can be daunting but the efforts are worth it. For bnd users that use the workspace model the advantages will flow freely.
+ + diff --git a/org.bndtools.help/docs/chapters/300-launching.html b/org.bndtools.help/docs/chapters/300-launching.html new file mode 100644 index 0000000000..b56a6b7f4f --- /dev/null +++ b/org.bndtools.help/docs/chapters/300-launching.html @@ -0,0 +1,313 @@ + + + + +bnd integrates an OSGi launcher. This launcher will start a framework and then install and start a list of bundles. Launch descriptions are defined in a bndrun file. (A bnd.bnd file can actually also act as a bndrun file.) The bndrun file inherits properties from the workspace, not the profile.
+The launching environment is described with a number of instructions that start with -run.
-runfw — Specify the run framework in repository format-runsystemcapabilities — Capabilities added to the environment-runbundles — A list of bundles to install in repository format-runvm — VM options. Use a comma to separate different options. E.g. -Dx=4, -Dy=5.-runproperties — System properties for the framework to create-runpath — A list of jars (not required to be bundles) that are put on the classpath and available from the system bundle. Any Export-Package and Provide-Capabilityheaders are provided as packages/capabilities from the framework. The -runpath can also override the default launcher.-runsystempackages — An Export-Package list for packages exported by the system bundle.-runkeep – Keep the framework working directory. That is, do not clean at start up-runstorage – The working directory-runnoreferences – Do not use the reference: scheme when installing. (Sometimes required on Windows).Additional properties can be specified, and can be inherited from the workspace, that are specific for a launcher or are used for exporting a bndrun to an executable format like OSGi Subsystems, KARs, WARs, or executable JARs.
+Launchers are not build into bnd, the actual launching strategy is parameterized. A launcher is associated with a bnd or bndrun file by placing a JAR on the -runpath. A JAR should have a Launcher-Plugin header to be a launcher. If no launcher is found on the -runpath then the built-in biz.aQute.launcher will be used.
The plugin maps the bnd model specified in a bndrun or bnd file to an external execution. In general this plugin launches or contacts a VM, installs the -runpath, installs -runbundles, fires up the bundles. The plugin is also called when the bundles or settings have changed so that it can dynamically update the bundles on the running VM.
There are currently two launchers in the bnd repository:
+biz.aQute.launcher – Default launcher. This launcher starts a VM on the local machine and keeps it synchronized with the changes in the IDE. The launcher can also create an executable JAR.biz.aQute.remote.launcher – A launcher that can communicate with a remote VM, optionally installs a -runpath (among which a framework), and then synchronizes the -runbundles and a number of other properties with the remote VM.The default launcher in bnd. It creates a new VM with the given options, creates a framework using the OSGi launching API, and then manages the bundles on this framework with the OSGi launching API. It can update the remote framework in real time by changing a properties file that is watched for by the launcher class running in the remote framework.
+-runfw: org.apache.felix.framework;version='[4,5)'
+-runbundles: \
+ org.apache.felix.shell,\
+ org.apache.felix.shell.tui,\
+ org.apache.felix.scr,\
+ org.apache.felix.http.jetty,\
+ org.apache.felix.configadmin,\
+ org.apache.felix.metatype,\
+ org.apache.felix.log,\
+ org.apache.felix.webconsole,\
+ osgi.cmpn,\
+ aQute.xray.badbundle;version=latest,\
+ aQute.xray.plugin;version=latest,\
+ aQute.xray.hello;version=latest,\
+ com.springsource.org.apache.commons.fileupload;version=1.2.1,\
+ com.springsource.org.apache.commons.io;version=1.4.0,\
+ com.springsource.org.json;version=1.0.0
+
+-runproperties: org.osgi.service.http.port=8080
+
+-runrequires:\
+ bundle:(symbolicname=org.apache.felix.shell),\
+ bundle:(symbolicname=org.apache.felix.shell.tui),\
+ bundle:(symbolicname=org.apache.felix.webconsole),\
+ bundle:(symbolicname=org.apache.felix.configadmin),\
+ bundle:(symbolicname=org.apache.felix.metatype),\
+ bundle:(symbolicname=org.apache.felix.log),\
+ bundle:(&(symbolicname=osgi.cmpn)(version>=4.2)),\
+ bundle:(&(symbolicname=org.apache.felix.scr)(version>=1.6.0))
+The launcher analyzes the -runpath JARs. Any additional capabilities in the manifest (packages and Provide Capability headers) in these JARs are automatically added to the framework.
There are cases where you are working with a recent 7.x release of bnd / bndtools but you want to export an executable .jar which contains e.g. the launcher of bnd 6.4.1 which is compatible with JDK-8 (because newer 7.x bnd and launcher requires JDK-17 minimum). +In this you can do the following:
+biz.aQute.bnd:biz.aQute.launcher:6.4.1 in one of your repositories.bndrun like this:# myapp.bndrun
+-runpath: ${repo;biz.aQute.bnd:biz.aQute.launcher;[6.4.1,6.4.2)};version=file
+The launcher registers a service with object class Object that provides some runtime information. The following properties are set on this service:
launcher.arguments – The arguments passed to the main method.launcher.properties – The properties handed to the launcher. These properties are modified by any overriding properties that were set from the command line.launcher.ready – A boolean set to true, indicating the launcher has done all its work.service.ranking – Set to -1000The launcher supports embedded activators. These are like normal Bundle Actviator classes but are instead found on the -runpath. Any bundle that has the header Embedded-Activator will be started. The start can happen at 3 points that are identified with a static field in the Embedded Activator class. This field is called IMMEDIATE. For example:
public class MyEmbeddedActivator implements BundleActivator {
+ public static String IMMEDIATE = "AFTER_FRAMEWORK_INIT";
+ ...
+}
+The IMMEDIATE field can have the following values:
"AFTER_FRAMEWORK_INIT" – The Embedded Activator is called after the Framework is initialized but before the framework is started. This means that no bundles are started yet."BEFORE_BUNDLES_START" – The Embedded Activator is called after the framework has been started but before the bundles are explicitly started by the launcher. This will always happening in start level 1. If the framework was started from an existing configuration then any bundles in start level 1 that were persistently started will therefore have been started before the Embedded Activator is started. The launcher starts bundles persistently so if the same configuration is restarted they will be started after the framework is started."AFTER_BUNDLES_START" – Will start the Embedded Activators after all bundles have been persistently started. Since this happens at start level 1, some bundles in higher start levels will not be active.The reason strings are used is to not require the need for extraneous types on the executable's class path. If a string in IMMEDIATE is used that is not part of the previous one then a message must be logged. The behavior will then be "AFTER_BUNDLES_START". Other strings are reserved for future extensions.
For example:
+public class MyActivator implements BundleActivator {
+ public static String IMMEDIATE = "BEFORE_BUNDLES_START";
+
+ public void start(BundleContext context) {}
+
+ public void stop(BundleContext context) {}
+}
+
+bnd.bnd:
+ Embedded-Activator: com.example.MyActivator
+For backward compatibility reason the IMMEDIATE field the launcher will also recognize a boolean field.
true – Corresponds to the String BEFORE_BUNDLES_START. false – Will be the "AFTER_BUNDLES_START" caseIt is recommended to update to one of the strings instead of the boolean and not use this pattern in new setups.
+The -runbundles instruction supports an startlevel attribute. If one or more of the bundles listed in the -runbundles instruction
+uses the startlevel attribute then the launcher will assign a startlevel to each bundle. This is currently supported for the
+normal launcher and not for Launchpad nor the remote launcher.
If a bundle has a startlevel attribute then this must be an integer > 0, otherwise it is ignored. Bundles that have no
+startlevel attribute will be assign the maximum assigned startlevel attribute + 1. For example, given the following
+bundles:
-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)';startlevel=10,\
+ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+ ...
+ osgi.enroute.twitter.bootstrap.webresource;version='[3.3.5,3.3.6)';startlevel=12,\
+ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)'
+The org.apache.felix.configadmin, org.apache.felix.http.servlet-api, and osgi.enroute.web.simple.provider do not specify
+a startlevel attribute and will therefore be assigned to start level 13. This value is picked because max(10,12)= 12 + 1 = 13.
Startlevels are assigned before the framework is started, they are updated on the fly if during a debug session the setup changes.
+Normally in OSGi the begining start level is selected with the system property org.osgi.framework.startlevel.beginning. If
+this -runproperty is not set then the launcher will set this property before starting the framework to the maximum of
+specified levels + 2. If a start level management agent is present then this property should be set, the launcher will
+then not interfere.
The bundles are started at during start level 1.
+For example, a management agent that manages start levels is placed in start level 1 and all other bundles are placed +at start level 100.
+-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)';startlevel=100,\
+ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+ org.example.management.agent;startlevel=1
+ osgi.enroute.twitter.bootstrap.webresource;version='[3.3.5,3.3.6)';startlevel=100,\
+ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)'
+The -runproperties specify a begining of 1:
-runproperties: \
+ org.osgi.framework.startlevel.beginning=1
+The launcher will install all bundles and assign them their start level. The framework is then started and moves +to level 1. This starts the management agent. This management agent will then move the start level higher to finally +level 100.
+Any bndrun file can be packaged by the launcher plugin. This creates an executable JAR that will contain all its dependencies.
+$ bnd package xyz.bndrun
+You can then execute the jar:
+$ java -jar xyz.jar
+You can override all the properties that are embedded in the executable JAR with the -Dlauncher.properties=<file> option of the Java VM. Instead of consulting its internal properties file, the launcher will read the given file. However, you can also set individual System properties with the -D option that override a specific launching constant.
launch.storage.dir – (-runstorage) Sets the directory for the framework directory. When org.osgi.framework.storage is also set, then the OSGi property has priority.launch.keep – Keeps the OSGi Framework storage aroundlaunch.system.packages – Extra system packages launch.system.capabilities – Extra system capabilitieslaunch.trace – Trace the launcher's progresslaunch.timeout – Abort after timeout millisecondslaunch.name – Name of the executable (normally project name)launch.noreferences – Do not use the reference: scheme (-runnoreferences)launch.notificationPort – A port to send errors toFor example, if you want to run your executable in trace mode:
+$ java -Dlaunch.trace=true xyz.jar
+An executable JAR can be unzipped in a directory. It will then include a Windows and Linuxy shell script to start the executable in expanded form. In the expanded form, the framework will try to use reference: URLs to install bundles when not on Windows. (Windows and reference URLs do not work well because Windows keeps files locked.)
If no reference URLs should be used, the -runnoreferences=true instruction can be set.
Framework properties can be set using the -runproperties instruction. Framework properties do not include the system properties but any property set using -runproperties can be overridden with a system property. That is, it is impossible to set a property using the -D on the Java command line unless it was prior given a default in the bndrun file.
For example:
+bndrun:
+-runproperties: foo=3
+
+
+$ java -Dfoo=4 -Dbar=1 xyz.jar
+In this example, the xyz app will see foo=4 but bar will not be a framework property.
If the framework stops the launcher will exit. It will set a system exit code that reflects the event type from that the framework returned. The shell script that started the launcher can take the system exit code into account to restart the framework in certain cases.
+OSGi FrameworkEvent Launcher Constant Value
+STOPPED STOPPED -9
+WAIT_TIMEDOUT TIMEDOUT -3
+ERROR ERROR -2
+WARNING WARNING -1
+STOPPED_BOOTCLASSPATH_MODIFIED STOPPED_UPDATE -4
+STOPPED_UPDATE STOPPED_UPDATE -4
+The launcher therefore returns the process exit code UPDATE_NEEDED(-4) when it requires an update. This was chosen over doing an in-process update because it is much safer. So the launching script should look something like:
+do {
+ bnd run app.bndrun
+} while ($?==-4)
+The purpose of the aQute Remote project is to provide remote debugging support for bnd projects. It can be used to debug bundles and bndrun files in a remote machine running an OSGi framework with an agent installed on it; it can also install a framework on a remote machine before it uses the agent. The architecture is heavily optimized to run on small remote machines.
+This project is the bnd remote launcher. It consists of the following artifacts:
+biz.aQute.remote.launcher – The actual launcher. You can use the biz.aQute.remote.launcher by placing it on the -runpath. This will override the default launcher. It contains the agent and the bnd launcher plugin.biz.aQute.remote.agent – The agent that must run as a bundle on a framework, or alternatively, runs on the framework side of the classpath and uses the framework's Bundle Context. That is, you can put this file on the -runpath of the biz.aQute.launcher bndrun and bnd files.biz.aQute.remote.main – An executable JAR.The biz.aQute.remote.launcher should be placed on the -runpath in a bnd or bndrun file. The remote plugin expects a -runremote instruction in the parent file. This property has the following syntax:
-runremote ::= remote (',' remote)*
+remote ::= NAME ( ';' aspect '=' value ) *
+aspect ::= 'jdb' | 'shell' | 'host' | 'agent' | `timeout`
+It is possible to specity multiple remote clauses. All sections are started simultaneously. The aspects are described in the following sections:
+jdb – The JDB debug port. This is the port that will be opened by the debugger in the IDE. If this aspect is not set, a number is assigned from 16043, incremented for every session. The debugger will wait until this port becomes available on the required host.host – The name of the remote host. Default localhost. Notice that for some Unixes there is a confusion what localhost is, some Linux variants make localhost 127.0.1.1 while the original is 127.0.0.1. So better make sure.shell – The shell that should be used. There are a number of possibilities for this value++1 – A TCP port. The launcher will attach the port from the remote host and forward any I/O.
+
agent – The port on which the agent is listening, the default is ${aQute.agent.server.port}.timeout – Timeout in seconds for the debug connectionAdditionally, the communication can be traced by setting the aQute.remote.util.Link.trace system property to true on the target system.
An example remote bndrun file:
+local = \
+ local; \
+ shell = 4003; \
+ jdb = 1044; \
+ host = localhost
+
+-runremote: ${local}
+-runfw: org.apache.felix.framework;version='[4,5)'
+-runee: JavaSE-1.8
+-runproperties: gosh.args=--noshutdown, osgi.shell.telnet.port=4003
+
+-runpath: biz.aQute.remote.launcher;version=latest
+-runrequires:\
+ osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\
+ osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
+
+-runbundles:\
+ launch.simple.really;version=latest, \
+ org.apache.felix.gogo.shell,\
+ org.apache.felix.gogo.command,\
+ org.apache.felix.gogo.runtime, \
+ org.apache.felix.shell.remote
+One example where a javaagent is used is the JaCoCo Java Code Coverage Library. +The following example shows how Jacoco v0.8.5 can be started by adding the -javaagent parameter via -runvm as a VM-option.
+create new myapp.coverage.bndrun which inherits from parent myapp.bndrun
+-include: myapp.bndrun
+
+-runvm.coverage: -javaagent:"${repo;org.jacoco:org.jacoco.agent:jar:runtime;latest}=output=tcpserver,jmx=true"
+In Eclipse open the myapp.coverage.bndrun and start your application.
+A few notes about the example:
+the additional parameters =output=tcpserver,jmx=true are JaCoCo-specific to start the agent as tcpserver and also expose functionality via JMX
+runvm.coverage is a merged instruction (because of the suffix .coverage). It gets merged with a -runvm parameter from the included parent myapp.bndrun. Advantage: Avoid repeating all the VM-options the from the parent -runvm again, but just specify the additional parameters.
+One of the primary authors of bnd has always objected to startlevels. His motivation was twofold:
+With this disclaimer out of the way, there are actually cases where startlevels are quite important to +improve the non-functional aspects.
+Run configurations in bnd are described in bndrun files. The list of bundles to run are listed in the -runbundles
+instruction. The runbundles instruction has a startlevel attribute that specifies the startlevel of each bundle.
Since the -runbundles instruction is frequently calculated by the resolver support in bnd it is not possible to manually
+assign the startlevel attributes to specific bundles. For this reason there is a decorator support in bnd for some selected
+instructions. A decorator is a header with the same name but ending with a plus sign (+). This instruction acts like a selector
+and can be used to assign startlevel attributes on the -runbundles.
For example:
+-runbundles: \
+ org.apache.servicemix.bundles.junit;version='[4.13.0,5)',\
+ org.apache.felix.log, \
+ demo;version='[1.0.0,1.0.1)'
+
+-runbundles+: \
+ demo;startlevel=11,\
+ *;startlevel=99
+The launcher is responsible for installing the bundles after starting or communicating with a framework. The
+default bnd launcher will assign each bundle a startlevel, where bundles that do not have a specified startlevel are
+assigned one level higher than the maximum specified levels.
The launcher supports narrow managed mode or all via the [-launcher] instruction. In narrow mode, the start levels
+will only be assigned to the scope, the set of bundles listed in the run bundles. In the all mode, all bundles
+that are installed at launch time will be assigned the default start level.
The default launcher will then move the framework start level to 2 higher than the highest specified start level.
+The resolve support in bnd can automatically assign startlevel attributes to the -runbundles based on different ordering
+strategies. There are two strategies that use the dependency graph. This graph can be sorted in topological order.
+This means that a resource is always listed ahead of any of its dependencies when there are no cycles.
The -runstartlevel instruction controls the ordering and assigned start levels.
+At the time of this writing it is not yet clear what the best strategy is, and it may depend.
+Ordering by the topological sort (a resource is followed by its dependencies) will start something like the log service +last and any applications bundles first. Although at first sight this feels wrong (the log service should be started first +to capture the events at startup) it does solve the jojo problem because one of the last bundles started will be the +Service Component Runtime that will activate all components. At that time all initialization should have taken place.
+Reversing the topplogical sort will start the something like SCR and log first because they have few or no dependencies. +Application bundles are then started latest.
+Traditionally start levels are managed by an application bundle. Shell commands can increase and decrease start levels. +For this reason, start levels often use nice numbers.
+Using the resolver the need for nice numbers is diminished since bnd is now taking care. The basic default model +is to assign bundles sequentially in selected order from an initial level stepping with 10. The -runstartlevel instruction +can provide the initial level and the step if desired.
+The launcher will by default move the framework then to a start level that includes any used start levels. This
+default behavior can be blocked by specifying the org.osgi.framework.startlevel.beginning property. If this
+property is set bnd will assume that there is an agent that will handle the runtime start levels.
A quick way to disable the start level handling by the launch is to set the property used to convey the default
+start level, launch.startlevel.default to 0. This will disable the complete start level handling regardless
+of other settings.
Deprecation: Note, that the default test biz.aQute.tester is deprecated as of bnd 7.2.0 and will be removed in 8.0.0.
+Please use and migrate your tests to JUnit 5 and use biz.aQute.tester.junit-platform going forward.
+The documentation below will be adjusted in the future.
This section describes how bnd implements a pluggable testing framework. With most tools that use bnd this information is hidden behind a pleasant GUI (sometimes). However, in certain cases it is necessary to understand how bnd handles testing.
+In the classic way testing consists of launching a new VM, dynamically loading the classes to be tested and then running the test framework on those. As this violates module boundaries as if they did not exist there is another way.
+In bnd there is a header that allows a bundle to define what tests it contains:
+Test-Cases ::= fqn+
+Such a bundle therefore exports a number of test cases to the environment. These test cases can then be executed by the tester. The default tester in bnd is the biz.aQute.tester bundle, but this can be overridden because the launcher and the tester are pluggable (see Other Tester Frameworks).
The default tester can run in one shot mode or in automatic mode. The one shot mode is specified using the tester.names property - the tester will run the specified tests and then exit. This mode is typically used by build tools, which set the tester.names property, run the tests, and then parse the results.
In automatic mode, the default tester creates a list of available bundles with the Test-Cases header set and executes all of them. Automatic mode will then end, or if the tester.continuous property is set it will continue running (known as continuous mode).
In continuous mode, every time a test bundle is started the tester will run that bundle's tests. Continuous mode is intended for developing test bundles. You just run a framework and edit your test bundle's code. Any changes are saved and deployed, triggering a restart of the bundles and hence a re-run of the tests.
+Test-Cases header AutomaticallyThe Test-Cases header can be set by hand but this can become a maintenance nightmare. A very useful macro is the ${classes} macro. This macro inspects the JAR and can find all classes that are considered test cases:
Test-Cases = ${classes;CONCRETE;EXTENDS;junit.framework.TestCase}
+This example looks for concrete classes that extend the JUnit 3 base class. For non JUnit 3 tests, you can use a naming convention:
+Test-Cases: ${classes;CONCRETE;PUBLIC;NAMED;*Test*}
+This will include all concrete public classes whose name includes the word Test.
biz.aQute.testerIf you do not explicitly specify the tester module to use, bnd will use biz.aQute.tester. The configuration of this module is as follows.
The default tester uses the project information to parameterize the tester's runtime component. However, it is also possible to set these runtime parameters explicitly with framework properties when you want to run the framework in automatic mode. The default tester obeys the following framework properties:
+| Property | +Default | +Description | +
|---|---|---|
tester.port |
+- | +The port to send the JUnit information to in a format defined by Eclipse. If this property is not set, no information is send but tests are still run. | +
tester.host |
+localhost |
+The host to send the JUnit information to in a formatted defined by Eclipse. If this property is not set localhost is assumed. | +
tester.names |
++ | Fully qualified names of classes to test. If this property is null automatic mode is chosen, otherwise these classes are tested and then the test exits. |
+
tester.dir |
+testdir |
+A directory to put the test reports. If the directory does not exist, no reports are generated. The default is testdir in the default directory. This directory is not automatically created so to see the results it is necessary to ensure such a directory exists. the files in the test directory are usable in Hudson/Jenkins as test reports |
+
tester.continuous |
+false |
+In automatic mode (ie, when no tester.names are set), continue watching the bundles and (re-)run a bundle's tests when it is started. |
+
tester.trace |
+false |
+Trace the test framework in detail. Default is false, must be set to true or false. | +
To setup an environment to test continuously, the following launcher configuration can be used:
+Test-Cases: ${classes;CONCRETE;EXTENDS;junit.framework.TestCase}
+-runfw: org.apache.felix.framework
+-buildpath: osgi.core;version='[4.1,5)', \
+ osgi.cmpn, \
+ junit.osgi
+Private-Package: org.example.tests
+-runtrace: true
+-runbundles: biz.aQute.junit
+-runproperties: \
+ tester.trace=true, \
+ tester.continuous=true, \
+ tester.dir=testdir
+The example setup creates a bundle containing the org.example.tests package and sets the Test-Cases header to all JUnit 3 test cases in that package. If you run this setup, it runs the project bundle with the biz.aQute.junit bundle. This tester bundle is parameterized with the tester.* properties to have trace on, continuous mode on, and to put the test reports in ./testdir.
You can find a bndtools project that shows this at Github.
+biz.aQute.tester.junit-platformAs of Bnd 5.0, bnd includes a new tester bundle biz.aQute.tester.junit-platform that supports JUnit 5.
As per the JUnit 5 documentation, JUnit 5 is comprised of three modules:
+biz.aQute.tester.junit-platform leverages JUnit Platform for discovering and launching tests. This tester will:
TestEngine implementations;Test-Cases header set, or with any classes matching the -tester.testnames property;biz.aQute.testerAt the moment, biz.aQute.tester.junit-platform supports most of the features of biz.aQute.tester, including continuous testing and XML reporting. There are a couple of notable exceptions:
biz.aQute.tester uses a number of mechanisms to inject a BundleContext into a running test case. Due to significant architectural differences between JUnit Platform and JUnit 3/4, this feature is not yet supported by biz.aQute.tester.junit-platform and may never be fully supported (or at least, not in a way that is 100% backward compatible). However, it is not difficult to work around this by manually fetching the bundle context in your tests using FrameworkUtil (eg, in a @Before method).junit-platform-launcher >= 1.4.0.In addition to JUnit Platform support, the new tester has some features that weren't available in the old tester, which makes its integration with Eclipse a bit more user-friendly:
+biz.aQute.tester, when running in continuous mode only the results of the first test run are displayed in Eclipse. With biz.aQute.junit-platform, if you select the Display JUnit results in IDE every time the tester reruns tests property in the launch configuration, then it will display the results afresh for every test run. This allows you to combine the power of continuous testing with the convenience of Eclipse's JUnit GUI.TestExecutionListenersJUnit Platform has a publicly-defined TestExecutionListener interface. As of Bnd 5.2, biz.aQute.tester.junit-platform has support for adding your own custom TestExecutionListener implementations: simply write a class that implements this interface, and then register it as a OSGi service before the test run starts. biz.aQute.tester.junit-platform will invoke the callback methods on your custom listener(s) as the tests are executing.
This feature can be useful if you want to do any custom reporting for your tests - it is likely to be much easier and more flexible than (eg) trying to manipulate XML files generated by the legacy XML reporting module.
+biz.aQute.tester.junit-platform-tester: biz.aQute.tester.junit-platform in your bnd file (see Other Tester Frameworks).biz.aQute.tester.junit-platform's dependencies are installed in your -runbundles.-runbundles.Bnd can help with the last two steps by adding the tester and engine bundles to -runrequires and using the resolver:
-runrequires: \
+ bnd.identity;id='org.junit.jupiter.engine',\
+ bnd.identity;id='org.junit.vintage.engine',\
+ bnd.identity;id='biz.aQute.tester.junit-platform'
+See the chapter on resolving for more information.
+Note that if you're only using JUnit 3/4, you can omit the -runrequires line for the Jupiter engine, and conversely if you're only using JUnit Jupiter you can omit the Vintage engine. Alternatively/additionally, if you have any other TestEngine implementation bundles available, you can list these here instead/as well (though this has not been tested).
As noted above, biz.aQute.tester.junit-platform requires JUnit Platform (and its dependencies) on the classpath, and if it is to do much that is useful it will also require at least one TestEngine. Bundled versions of these are part of Eclipse since Oxygen. You can include them in your workspace from:
-plugin.repository: \
+ aQute.bnd.repository.osgi.OSGiRepository;\
+ name="Eclipse 2018-12";\
+ locations="https://bndtools.jfrog.io/bndtools/bnd-build/eclipse/4.10/index.xml.gz";\
+ poll.time=-1;\
+ cache="${workspace}/cnf/cache/stable/Eclipse-2018-12"
+ -plugin.repository: \
+ aQute.bnd.repository.p2.provider.P2Repository;\
+ name="Eclipse Local";\
+ url="file:///path/to/eclipse/";\
+ location="${workspace}/cnf/cache/stable/EclipseLocal"
+Alternatively, it is not difficult to download the required (non-OSGi) modules from Maven Central and include them as-is on -runpath, or else (preferably) wrap them into bundles and include them in -runrequires/-runbundles.
+As of JUnit 5.6, the JUnit jars already have the OSGi metadata and so can be used as bundles
+direct from Maven Central.
Also note that unfortunately, due to a bug in biz.aQute.tester.junit-platform, Bndtools 5.0
+does not work with JUnit 5.5+. A fix is already available in the latest development snapshot, and we expect the
+fix to be included in a future Bndtools release (hopefully soon).
The biz.aQute.tester is a normal bundle that gets started from the launcher framework. However, before bnd chooses the default tester, it scans the classpath for a tester (set with -runpath) for JARs that have the following header set:
Tester-Plugin ::= fqn
+If no such tester is found on the -runpath it will look in the -tester instruction and loads that bundle:
-tester: biz.aQute.junit
+Otherwise it will use biz.aQute.tester (if it still can find it).
The Tester-Plugin header points to a class that must extend the aQute.bnd.build.ProjectTester class. This class is loaded in the bnd environment and not in the target environment. This ProjectTester plugin then gets a chance to configure the launcher as it sees fit. It can get properties from the project and set these in the Project Launcher so they can be picked up in the target environment.
As this is a quite specific area the rest of the documentation is found in the source code.
+For a long time bnd had biz.aQute.junit as the default tester. biz.aQute.junit has the same functionality as biz.aQute.tester, but with the following key differences:
biz.aQute.junit embedded the JUnit 3/4 classes and exported them. biz.aQute.tester imports the JUnit classes like any other bundle, giving you flexibility in which version you wish to use.biz.aQute.junit added itself to the -runpath and then executed the tests from there, making itself (and JUnit) part of the system bundle. In contrast, biz.aQute.tester adds itself to -runbundles.Unfortunately the design of biz.aQute.junit caused constraints between JUnit and bnd that was not good because JUnit itself is not directly a shining example of software engineering. :-( So for this reason, biz.aQute.tester (or the newer biz.aQute.tester.junit-platform) is generally preferred.
If for some reason you need to be backward compatible with the older model, set:
+-tester: biz.aQute.junit
+note This featue is in beta. Feedback welcome and expect a few deficiencies in documentation and usage.
+An OSGi framework poses special challenges to testing because it is necessary to start a framework instance for each +test. There has always been a bnd OSGi testing framework that was developed to test the OSGi specifications and reference +implementations. This testing framework packaged the tests as a bundle and had a special version of JUnit that could run +these tests from inside the framework. Actually quite powerful and it was fully integrated with Eclipse JUnit testing, +delivering identical output to the CI build tools. However, the use of a special JUnit runner excluded it for people that wanted +to use TestNG or other test frameworks.
+A second problem was that since tests ran inside the framework as a bundle they could not influence the setup of the OSGi +framework easily. A last problem was that tests shared the same framework which could result in ordering dependencies.
+Launchpad is a bnd runtime library that provides an API to launch a framework that is fully integrated with the +bnd workspace. It automatically exports the runtime class path via the framework bundle, ensuring there is a single class space for +the code on the class path and the code in the bundles. (This does require that bundles properly import their +exported packages.)
+Launchpad provides a builder that incrementally can build up the specifications of the framework. The builder can
+take bndrun files or bundle specifications in the same format as that are used to set the -buildpath or
+-runbundles in the bnd.bnd files. Once the information is setup, bnd will calculate the setup based on
+the classes in the test folder and launch an OSGi framework.
Once the framework is launched, Launchpad can then inject services and some key framework objects into annotated +fields. Each field can specify a timeout, target filter, and minimum cardinality. Injection can take place in any object but is +usually on the test instance.
+In the original OSGi testing support test bundles had to be created during the build. In the OSGi test cases for the Blueprint
+reference implementation more than 200 test bundles were used. Although the overhead was relatively small due to the bnd -make facility,
+it was still a nuisance because the information in a test case had to be synchronized in a bnd file in another directory. For
+this reason Launchpad contains a bundle builder. This bundle builder used bnd under the hood. It can use anything that a bnd
+sub bundle could use; it is build in the context of the project that contains the test classes and shares the same
+-buildpath. Bundles that are build with the bundle builder can actually leverage nested classes in the test class for
+Bundle Activator or component classes.
Overall this is a comprehensive library for testing OSGi projects in a Bndtools workspace.
+You need the biz.aQute.launchpad library on your -testpath.
bnd.bnd:
+...
+-buildpath: ...
+-testpath: \
+ osgi.enroute.junit.wrapper, \
+ biz.aQute.launchpad
+note The biz.aQute.launchpad is available in 4.2 but there are a few minor API changes that did not make it. You can +therefore use the snapshot version on JFrog or download the latest version and use:
+lib/biz.aQute.launchpad.jar;version=file
+The next step is to enable your workspace for launchpad. A bnd workspace can have a Remote Workspace Server and
+Launchpad needs it. You therefore need to add the following to your cnf/build.bnd file.
cnf/build.bnd:
+ -remoteworkspace true
+Using JUnit, we can now create a test. We start with creating a LaunchpadBuilder. This builder stores information about the to be started framework. It has many
+methods that usually align with the properties for a bnd.bnd file. The Javadoc contains the details. We could create this
+object in a @Before method and close it in an @After method but this object does not have to be closed. It contains only
+the settings.
LaunchpadBuilder builder = new LaunchpadBuilder()
+ .runfw("org.apache.felix.framework");
+For this first quick start test we inject a Bundle Context, the core OSGi object that allows us to interact with the framework.
+@Service
+BundleContext context;
+The method is a normal JUnit test method. We open the Launchpad in a try resource block. Closing the Launchpad object will
+shutdown the framework. The test is simply verifying that the injection has worked.
@Test
+public void quickStart() throws Exception {
+ try (Launchpad launchpad = builder.create()
+ .inject(this)) {
+ assertNotNull(context);
+ }
+}
+Voila! The first Launchpad test case.
+The Launchpad code can be used in different modes:
+To use the Launchpad Runner it is necessary to add an @RunWith annotation on your JUnit test class:
@RunWith(LaunchpadRunner.class)
+public class TestMyCode {
+
+ LaunchpadBuilder builder = new LaunchpadBuilder().runfw("org.apache.felix.framework").debug();
+
+ @Service
+ Launchpad launchpad;
+
+ @Test
+ public void testMyCode() {
+ launchpad.report();
+ }
+}
+The Launchpad Runner is in control to gather the tests and then execute them. The test gathering is handled via the +standard JUnit support. However, when the test must run, LaunchpadRunner creates a bundle that has the following qualities:
+The runner then launches a framework based on a LaunchpadBuilder that it finds in the the static field builder.
It then installs the bundles, and adds the test bundle. To execute a test, it loads the +class from the test bundle and finds the appropriate method, instantiates the class in an instance, runs the injector +on this object. and executes the methods.
+This mode is similar to the PAX Exam model. It has similar constraints. It does run the @Before and @After annotated
+methods but it cannot run the @BeforeClass and @AfterClass.
Launchpad is quite awesome to use but there are some pitfalls to take into account. It is strongly recommended +to read this section to get an idea how Launchpad handles class sharing between the test classes (which are on the +normal Java class path) and the classes in bundles. There is more going on than what one suspects looking at the +simplicity how it can be used. This section details the workings to make you aware of potential bugs and should +help in debugging problems. In general, the cleaner your code base, the better this all works. If you have a very messy +setup with lots of scripts, fragments, require bundle, and very wide code interfaces instead of services then this +might not be for you ...
+In Java, class are loaded from the classpath. The class path is a (usually very long) list of Jar files. When a class +needs to be loaded, Java searches al those Jars for that class, first one wins.
+In OSGi, this model is changed for a delegating model. Each bundle imports a set of packages and exports a set +of packages. This information is in a bundle's manifest. When an OSGi framework resolves a bundle, it wires these +imports to a corresponding export.
+When a test case gets started the driver (Eclipse, Gradle, etc.) launches a new Java VM. The class path for that
+VM will consist of all entries on the -buildpath, -testpath, and the main and test output folders. When the
+Launchpad builder is first called it will contact the Remote Workspace in the driver and request for an analysis of
+the test code. The Remote Workspace then uses the project setup to calculate a bundle that would export all the
+test code and its imports. The Launchpad Builder then makes the OSGi framework export all packages that that
+virtual test bundle would have exported. That is, any class visible from the test cases will be exported by
+the OSGi framework by default. It is possible to exclude exports using glob expressions or predicates from a test case.
+See the excludeExport() methods.
A bundle installed on the OSGi Framework should this see all the relevant classes from the class path instead of +from other bundles. The tricky case is when a bundle exports a package that is also available from the +class path. If this package is only exported then the framework cannot substitute it for the package from the +class path. In such a bad case the bundle that exports it will see its embedded version of the class while +the rest of the system sees the version from the class path. This can then result in a class cast exception +like:
+java.lang.ClassCastException: an instance of org.example.Foo cannot be assigned to org.example.Foo
+Although the names of those classes are identical, the problem is that they will be loaded by different class +loaders.
+Due to this setup there is very strict sensitivity to the version of the OSGi Framework packages and bundles. Most bnd projects
+have the OSGi framework packages on the -buildpath. The version of these packages can be lower than the version
+of the Framework because of backward compatibility. Actually, bnd generally recommends to compile against the
+lowest possible framework packages. However, with launchpad these packages will also be used by the Framework, there
+is unfortunately no good way around this. The consequence is that the -buildpath version of the Framework
+packages must match the exact version used by the implementation of the framework.
Launchpad will calculate the set of packages that are exported by the framework from the claspath. The so called
+org.osgi.framework.system.packages.extra. It calculates this by creating bundle from the test sources, adding
+all dependencies, and then exporting the full content. That export statement is then uses for org.osgi.framework.system.packages.extra.
However, this is generally too wide since it includes all dependencies, not just public dependencies. Version mismatches +can create nasty problems and sometimes the solution is to exclude exports.
+The Launchpad Builder provides a number of methods called excludeExport() that take either a glob or a predicate.
+The globs/predicates are then ran against the list of calculated export package names. Any matching entry is then
+not exported.
Launchpad b = new LaunchpadBuilder()
+ .excludeExports( "slf4j.*")
+ .create();
+If a bndrun file is the -excludeexports instruction can be placed in the bndrun file containing a list globs.
-excludeexports aQute.lib*, slf4j.*
+The Launchpad has a default name of the method and class that called create(). These names can be overwritten with
+create(name) and create(name,className). The actual name of Launchpad is set under the following framework
+property names:
launchpad.name
+launchpad.className
+Clearly the best part of Launchpad is that you can actually use real services and do not have +to mock them up. Many a test seems to mostly test their mocks.
+With Launchpad, real framework is running. You can inject services or register services.
+In the following example we register a service Foo and then verify if we can get it. We then
+unregister the service and see that it no longer exists.
interface Foo {}
+
+@Test
+public void services() throws Exception {
+ try (Launchpad launchpad = builder.create()) {
+
+ ServiceRegistration<Foo> register =
+ launchpad.register(Foo.class, new Foo() {});
+ Optional<Foo> s =
+ launchpad.waitForService(Foo.class, 100);
+ assertThat(s.isPresent()).isTrue();
+
+ register.unregister();
+
+ s = launchpad.waitForService(Foo.class, 100);
+ assertThat(s.isPresent()).isFalse();
+ }
+}
+The waitForService methods take a timeout in milliseconds. Their purpose is to provide some leeway
+during startup for the system to settle. If a service should be there then it the getService() methods
+can be used.
Injection is not automatic because in many cases you want to handle the setup of the framework before +you inject. Injection can also happen as often as you want. However, you first need to create and +start the framework.
+@Test
+public void inject() throws Exception {
+ try (Launchpad launchpad = builder.create()) {
+ ServiceRegistration<Foo> register = launchpad.register(Foo.class, new Foo() {});
+
+ class I {
+ @Service
+ Foo foo;
+ @Service
+ Bundle bundles[];
+ @Service
+ BundleContext context;
+ }
+ I inject = new I();
+ launchpad.inject(inject);
+ assertThat(inject.bundles).isNotEmpty();
+ }
+}
+Although Bundle-Activator's are not recommended to be used (they are singletons), they are very useful in +test cases. With Launchpad it is not necessary to to make a separate bundle, we can make a bundle with +an inner class as activator.
+We therefore first define the Bundle Activator as a static public inner class of the test class:
+public static class Activator implements BundleActivator {
+ @Override
+ public void start(BundleContext context) throws Exception {
+ System.out.println("Hello World");
+ }
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ System.out.println("Goodbye World");
+ }
+}
+The Launchpad class contains a special Bundle builder. This bundle builder is based on bnd and can can do everything that
+bnd can do in a bnd.bnd file. In this case we add the Bundle Activator and start it.
@Test
+public void activator() throws Exception {
+ try (Launchpad launchpad = builder.create()) {
+
+ Bundle start = launchpad.bundle()
+ .bundleActivator(Activator.class)
+ .start();
+ }
+}
+When the test is run the output is:
+Hello World
+Goodbye World
+Since the bundle builder can do anything bnd can do, we can also use inner classes for components. These inner
+classes must be static and public (this is an OSGi DS requirement). The following is an example of a class
+that depends on the Bar service.
interface Bar {
+ void bar();
+}
+
+@Component
+public static class C {
+ @Reference Bar bar;
+ @Activate void activate() { bar.bar(); }
+}
+
+@Test
+public void component() throws Exception {
+ try (Launchpad launchpad = builder
+ .bundles("org.apache.felix.log")
+ .bundles("org.apache.felix.scr")
+ .bundles("org.apache.felix.configadmin")
+ .create()) {
+
+ Bundle b = launchpad.component(C.class);
+ AtomicBoolean called = new AtomicBoolean(false);
+ launchpad.register(Bar.class, ()-> called.set(true) );
+ assertThat(called.get()).isTrue();
+ }
+}
+Adding a component will return a Bundle. Uninstalling the bundle will remove the component.
+Clearly there are lots of things that can go wrong. You can therefore activate the debug() on the builder or
+Launchpad. This will provide logging to the console.
@Test
+public void debug() {
+ try (Launchpad launchpad = builder
+ .debug()
+ .create()) {
+
+ }
+}
+If you run this the console will show a lot of diagnostics information.
+One of the great innovations in bndtools is the resolver. So far we've assembled the list of bundles to run ourselves.
+However, we can also use a bndrun file and bndrun files can be resolved.
@Test
+public void bndrun() {
+ try (Launchpad launchpad = builder
+ .bndrun("showit.bndrun")
+ .create()) {
+ }
+}
+If the bndrun file contains the following after resolving:
-runrequires: \
+ osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\
+ osgi.identity;filter:='(osgi.identity=biz.aQute.bnd.runtime.gogo)'
+
+
+-runbundles: \
+ org.apache.felix.gogo.runtime;version='[1.1.0,1.1.1)',\
+ org.apache.felix.gogo.shell;version='[1.1.0,1.1.1)',\
+ biz.aQute.bnd.runtime.gogo;version=snapshot,\
+ org.apache.felix.log;version='[1.0.1,1.0.2)'
+-runfw: org.apache.felix.framework;version='[5.6.10,5.6.10]'
+-runee: JavaSE-1.8
+Then we see the following output:
+Welcome to Apache Felix Gogo
+g!
+The Launchpad object contains a special bundle builder. It provides the same capabilities that bnd already has when
+it creates bundles. You can use all the facilities that you can use in the bnd.bnd file. Export-Package, Import-Package,
+-includeresource, etc.
The following example shows how to create a bundle with a a special header.
+@Test
+public void bundles() throws Exception {
+ try (Launchpad launchpad = builder.create()) {
+ Bundle b = launchpad.bundle()
+ .header("FooBar", "1")
+ .install();
+ String string = b.getHeaders()
+ .get("FooBar");
+ assertThat(string).isEqualTo("1");
+ }
+}
+When you create a bundle from the Launchpad class then by default you inherit nothing from the enironment. However,
+it is possible to set the parent of the builder. The parent(String) or parent(File) method can set multiple
+parents.
parent ::= FILE* ( WORKSPACE | PROJECT )?
+WORKSPACE – Inherit from the workspace, excludes inheriting from the project and must be specified last in the set of parents.PROJECT – Inherit from the workspace, excludes inheriting from the project and must be specified last in the set of parents.FILE – A file path with forward slashes on all platforms. This must point to a bnd/properties file. The order of the parents is important. Earlier bnd files override the same named value in later bnd files.
+Example:
+Bundle b = lp.bundle().parent('foo.bnd').parent(WORKSPACE).start();
+In some scenarios you'd like to hide a service so you can override it with a mocked version. Hiding in OSGi +can be achieved with the [Servic Hooks] services. The easiest way is to hide a service via the Launchpad Builder.
+ LaunchpadBuilder builder = new LaunchpadBuilder().runfw("org.apache.felix.framework").hide(SomeService.class);
+If you now create a framework, all services not registered via Launchpad will be invisible to all bundles. That is, +only services registered through Launchpad can be seen by the other bundles in the OSGi framework.
+@Test
+public void testHidingViaBuilder() throws Exception {
+ try (Launchpad fw = builder.runfw("org.apache.felix.framework")
+ .create()) {
+
+ boolean isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+
+ fw.framework.getBundleContext()
+ .registerService(String.class, "fw", null);
+
+ isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+
+ ServiceRegistration<String> visibleToAllViaTestbundle = fw.register(String.class, "Hello");
+
+ assertThat(fw.getServices(String.class)).containsOnly("Hello");
+ visibleToAllViaTestbundle.unregister();
+
+ isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+ }
+}
+Although hiding via the Launchpad Builder is the easiest way to hide services, it has the disadvantage that all
+tests hide the same service(s). It is also possible to handle the service hiding in a more controlled way
+by hiding via the Launchpad object. Using this function does require a bit of orchestration. Once you hide
+a service it becomes invisible to bundles that look for that service later. However, bundles that already
+obtained this service will not lose sight of it. It is therefore necessary to hide a service before the
+corresponding framework is started. Since the default is automatic start, the automatic start must be
+disabled with the nostart() method on the builder.
After the framework is then created. the service is hidden and then the framework is started.
+@Test
+public void testHiding() throws Exception {
+ try (Launchpad fw = builder.runfw("org.apache.felix.framework")
+ .nostart()
+ .create()) {
+
+ @SuppressWarnings("resource")
+ Closeable hide = fw.hide(String.class);
+ fw.start();
+
+ boolean isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+
+ fw.framework.getBundleContext()
+ .registerService(String.class, "fw", null);
+
+ isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+
+ ServiceRegistration<String> visibleToAllViaTestbundle = fw.register(String.class, "Hello");
+
+ assertThat(fw.getServices(String.class)).containsOnly("Hello");
+ visibleToAllViaTestbundle.unregister();
+
+ isHidden = fw.getServices(String.class)
+ .isEmpty();
+ assertThat(isHidden).isTrue();
+
+ hide.close();
+ assertThat(fw.getServices(String.class)).containsOnly("fw");
+ }
+}
+As you can see from the test code, the hide method in this case returns a Closeable. This object can be used to
+remove the hiding of the given service.
To diagnose any issues it is important to realize that there are some special rules around which bundle does what. Launchpad +has the Bundle Context of the OSGi Framework as well as a special test bundle. The test bundle is empty but it used to have +a Bundle Context for the test code that runs outside the Framework. When you hide a service it will register +Service Hooks that only let services from the test bundle pass through, all other services of the given type are removed +from visibility.
+Closeable Launchpad.hide(SomeService.class);
+The service will remain hidden until you close the Closeable.
Java developers face a challenge today of building JPMS libraries, let alone when adding a secondary goal that those libraries be usable OSGi bundles. Accuracy and consistency of metadata can often become a problem reducing the time spent focusing on more productive aspect of the library.
+A key OSGi innovation is the use of annotations to significantly reduce (in many cases to completely eliminate) the configuration needed to describe an OSGi bundle in build descriptors. See Bundle Annotations.
+Just one of the additional benefits of using bnd is the ability to generate the module-info.class along side all of the traditional OSGi metadata.
When applying the instruction -jpms-module-info bnd will collect any relevant information and using several heuristics automatically generates the module-info.class.
When calculating the module name the following is considered:
+-jpms-module-info instruction contains a key, the key is used as the module name
+ e.g. -jpms-module-info: foo.module, the module name is foo.moduleAutomatic-Module-Name header is found in the manifest, the value is used as the module nameBundle-SymbolicName (as calculated) is used as the module nameWhen calculating the module version, the following is considered:
+-jpms-module-info instruction contains a key having a version attribute, the version attribute value is used as the module version
+ e.g. -jpms-module-info: foo.module;version=1.2.3, the module version is 1.2.3Bundle-Version (as calculated) is used as the module versionWhen calculating the module access, the following is considered:
+-jpms-module-info instruction contains a key having an access attribute, the access attribute value is used as the module access. Multiple comma separate values are allowed but quotes are required.
+ e.g. the module access is 0x0020 (OPEN):
+ properties
+ -jpms-module-info: foo.module;access=0x0020
+ With multiple flags:
+ properties
+ -jpms-module-info: foo.module;access='OPEN,SYNTHETIC'Legal values are:
+ * OPEN or open, 0x0020, 32
+ * SYNTHETIC or synthetic, 0x1000, 4096
+ * MANDATED or mandated, 0x8000, 32768
Require-Capability contains any capability in the osgi.extender namespace, the module access is open0Note that for the above rules, the earliest matching rule wins.
+When calculating the module requires the following is considered:
+If the -jpms-module-info instruction contains a key having a modules attribute, the modules attribute value is first split on commas (,) and each segment is added as a raw required module name
+ e.g. -jpms-module-info: foo.module;modules='java.desktop,java.logging', the modules java.desktop, and java.logging are added to module requires
In addition, if the -jpms-module-info instruction contains a key having a ee attribute, the ee attribute indicates the Java module name mapping table to use for Java SE packages using bnd's aQute.bnd.build.model.EE definitions which define a set of Java module name mapping tables keyed by EE.
+ e.g. -jpms-module-info: foo.module;ee=JavaSE-10, bnd will use the Java module name mapping table for Java SE 10 when determining module name for a given Java SE package
If no ee attribute is specified, bnd will use the Java module name mapping table for Java SE 11 when determining module name for a given Java SE package
Note: Non-Java SE packages are associated with module names by indexing all packages on the classpath of the bnd analyzer where the providing jar's module's name is:
module-info.classAutomatic-Module-Name-\d)Bnd will set the access to transitive if any package exported by the bundle has a uses constraint on a package of the required module.
Bnd will set the access to static if the module is specified in the -jpms-module-info instruction and does not actual have any imports.
Bnd will set the access to static if all the packages imported from the module are any combination of resolution:=optional, resolution:=dynamic or match the Dynamic-ImportPackage instruction.
Bnd does not currently track a require's version.
Module exports will be mapped from all OSGi exported packages by default which can be managed easily with use of the Bundle Annotation @org.osgi.annotation.bundle.Export on package-info.java.
@org.osgi.annotation.bundle.Export
+package com.acme.foo;
+Targeted exports (via the exports .. to .. clause) are supported with use of the @aQute.bnd.annotation.jpms.ExportTo. This annotation specifies the module name(s) to which a exported is targeted.
@org.osgi.annotation.bundle.Export
+@aQute.bnd.annotation.jpms.ExportTo("com.acme.bar")
+package com.acme.foo;
+Note: The @ExportTo annotation is only relevant in conjunction with the @Export annotation.
Module opens are supported with use of the @aQute.bnd.annotation.jpms.Open annotation on package-info.java. This annotation optionally specifies the module name(s) to which the opens is targeted.
@aQute.bnd.annotation.jpms.Open
+package com.acme.foo;
+Module uses are supported transparently with use of the bnd @aQute.bnd.annotation.spi.ServiceConsumer SPI Annotation.
Module provides are supported transparently with use of the bnd @aQute.bnd.annotation.spi.ServiceProvider SPI Annotation.
The module main class attribute is supported with use of the @aQute.bnd.annotation.jpms.MainClass annotation applied to the main class's Java type.
@aQute.bnd.annotation.jpms.MainClass
+public class Main {
+ public static void main(String... args) {...}
+}
+Bnd's module-info.class generation is supported when building with Java 8 or higher. (Yes! I did say Java 8.)
There are scenarios where the heuristics used by bnd don't give the desired result because the necessary information is not available or is incorrect.
+The -jpms-module-info-options instruction provides some capabilities to help the developer handle these scenarios. The instruction uses the package header syntax similar to many other bnd instructions. The keys of these instructions are module names and there are 4 available attributes. They are:
substitute - If bnd generates a module name matching the value of this attribute it should be substituted with the key of the instruction.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;substitute="geronimo-jcdi_2.0_spec"
+ means that if bnd calculates the module name to be geronimo-jcdi_2.0_spec it should replace it with java.enterprise
ignore - If the attribute ignore="true" is found the require matching the key of the instruction will not be added.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;ignore="true"
means ignore the module java.enterprise
static - If the attribute static="true|false" is found the access of the module matching the key of the instruction will be set to match.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;static="true"
means make the require for module java.enterprise static
transitive - If the attribute transitive="true|false" is found the access of the module matching the key of the instruction will be set to match.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;transitive="true"
means make the require for module java.enterprise transitive
The following is an example with multiple attributes and instructions:
+-jpms-module-info-options: \
+ java.enterprise;substitute="geronimo-jcdi_2.0_spec";static=true;transitive=true,\
+ java.management;ignore=true;
+Bnd's export functionality allows for the creation of executable jars which contain fully assembled applications whose only external dependency is a JDK.
When the -jpms-module-info instruction is used in a bndrun file that is the target of the export function the executable jar that is created will contain a generated module-info.class containing whatever information bnd's JPMS heuristics could glean from the assembly.
e.g.
+-jpms-module-info: \
+ ${project.artifactId};\
+ version=${project.version};\
+ ee=JavaSE-${java.specification.version}
+The example above produces a module-info.class whose module id comes from the property project.artifactId, version from the property project.version and ee from the property java.specification.version.
When the -jpms-module-info instruction is used in a bndrun file that is the target of the export function bnd launcher's main class will be applied to the module info implicitly.
OSGi developers face a challenge when using third-party libraries that +are not supplied as OSGi bundles. Though an increasing number of +libraries are available from their original sources as OSGi bundles, and +a large number are available as wrapped OSGi bundles from external +repositories, it is still sometimes necessary to build such a wrapper +ourselves. This technical note details an approach to OSGi bundle +production using only command line tools.
+This article details a simple and repeatable process to wrap +arbitrary Java libraries as OSGi bundles, using bnd as a command line +tool.
+As a running example, the JDOM library version 1.1.2 will be wrapped as +an OSGi bundle.
+NB: Many of the tasks described here can be more easily performed with a +full-featured OSGi IDE such as Bndtools. However, this document is +intended for users who perform these tasks infrequently and do not wish +to download an IDE; instead a single, lightweight +command-line tool is used.
+Bnd generates the Import-Package statement of the output bundle via an
+extremely thorough inspection of the compiled Java. Every bytecode
+instruction of every class file inside the bundle is processed to
+discover dependencies on external packages. Usually the result of this
+inspection is more accurate than we would be able to achieve by manually
+providing the Import-Package statement.
Unfortunately when wrapping third-party libraries it is sometimes not
+sufficient to simply accept the generated Import-Package statement:
+the result may need to be fine-tuned. This is because many third-party
+libraries contain dependencies that are out of place, often due to
+errors resulting from a lack of good modular practices.
For example:
+Bnd detects dependencies statically by inspecting all code in the +library; it cannot determine which parts of the library are reachable. +For example a common error is to include JUnit test cases in a library +JAR, resulting in dependencies on JUnit. Unless fixed, the bundle will +only be usable in a runtime environment where JUnit is also present, +i.e., we will have to ship a copy of JUnit to our end users.
+The problem of checking for and correcting such problems represents the +bulk of the manual effort required in what is otherwise a fairly +automatic process.
+We assume that the JDOM library has been downloaded, and jdom.jar is
+available in the current directory.
In order to wrap as a bundle using bnd, we need an initial "recipe".
+Create a file named jdom.bnd containing the following:
-classpath: jdom.jar
+Bundle-SymbolicName: org.jdom
+ver: 1.1.2
+-output: ${bsn}-${ver}.jar
+Bundle-Version: ${ver}
+Export-Package: *;version=${ver}
+This is a bnd descriptor, and it instructs bnd how to generate the OSGi +bundle. To summarize the features used:
+To generate the bundle: bnd reports the name of the generated file +(org.jdom-1.1.2.jar), the number of files contained (79) and its size in +bytes (151K). We refer to this bundle as the initial wrapping.
+The intial wrapping may contain dependency errors as described in the
+introduction. Therefore we must examine the Import-Package statement
+as generated by bnd. Unfortunately, direct viewing of the MANIFEST.MF
+can be difficult due to the unusual formatting and line-wrapping rules
+of the manifest file format that make it quite inaccessible. For
+example:
$ bnd jdom.bnd
+org.jdom-1.1.2.jar 79 154490
+Import-Package: javax.xml.parsers,javax.xml.transform,javax.xml.transf
+ orm.sax,javax.xml.transform.stream,oracle.xml.parser,oracle.xml.parse
+ r.v2,org.apache.xerces.dom,org.apache.xerces.parsers,org.jaxen,org.ja
+ xen.jdom,org.jdom;version="[1.1,2)",org.jdom.adapters;version="[1.1,2
+ )",org.jdom.filter;version="[1.1,2)",org.jdom.input;version="[1.1,2)"
+ ,org.jdom.output;version="[1.1,2)",org.jdom.transform;version="[1.1,2
+ )",org.jdom.xpath;version="[1.1,2)",org.w3c.dom,org.xml.sax,org.xml.s
+ ax.ext,org.xml.sax.helpers
+Since this is so unreadable, Bnd offers a print command that formats in
+the manifest of a specified bundle JAR. We can request Bnd to print only
+the imports and exports by using the -impexp switch:
$ bnd print -impexp org.jdom-1.1.2.jar
+[IMPEXP]
+Import-Package
+ javax.xml.parsers
+ javax.xml.transform
+ javax.xml.transform.sax
+ javax.xml.transform.stream
+ oracle.xml.parser
+ oracle.xml.parser.v2
+ org.apache.xerces.dom
+ org.apache.xerces.parsers
+ org.jaxen
+ org.jaxen.jdom
+ org.jdom {version=[1.1,2)}
+ org.jdom.input {version=[1.1,2)}
+ org.w3c.dom
+ org.xml.sax
+ org.xml.sax.ext
+ org.xml.sax.helpers
+Export-Package
+ org.jdom {version=1.1.2, imported-as=[1.1,2)}
+ org.jdom.adapters {version=1.1.2}
+ org.jdom.filter {version=1.1.2}
+ org.jdom.input {version=1.1.2, imported-as=[1.1,2)}
+ org.jdom.output {version=1.1.2}
+ org.jdom.transform {version=1.1.2}
+ org.jdom.xpath {version=1.1.2}
+Reviewing the imports, we see that most of them come +from JRE packages. However there are three groups of dependencies that +may cause problems: the Oracle XML parser; the Xerces XML parser; and +the Jaxen XPath library. Unless something is done to fix these, our JDOM +bundle will not work unless all three dependencies are present at +runtime.
+First we consider the Oracle XML parser dependency. We anticipate that
+this probably derives from an optional feature, and can therefore be
+considered an optional dependency. Importing a package with the
+resolution:=optional directive allows our bundle to see the specified
+package at runtime if an exported for it is available, but it does not
+prevent the bundle from resolving in the event that an exported is not
+available.
To mark the two Oracle imports as optional, add the following line to
+the jdom.bnd file:
Import-Package: \
+ oracle.xml.*;resolution:=optional, \
+ *
+If we regenerate the bundle and again ask Bnd to print the imports and +exports, we will see that the Oracle dependencies are marked as +optional;
+[IMPEXP]
+Import-Package
+ ...
+ oracle.xml.parser {resolution:=optional}
+ oracle.xml.parser.v2 {resolution:=optional}
+ ...
+We can do the same for the Xerces packages:
+Import-Package: \
+ oracle.xml.*;resolution:=optional,\
+ org.apache.xerces.*;resolution:=optional,\
+ *
+NB: the final * in the Import-Package statements is
+extremely important. Without this, all other dependencies detected
+by Bnd will be omitted from the final manifest.
In some cases the root cause of a dependency is unclear, or we may wish +to obtain further information on the causes of a dependency.
+JDOM depends on Jaxen, an XPath evaluation library. However not all
+use-cases for JDOM involve evaluating XPath expressions, so this may be
+an optional dependency. To get further information to help us make this
+decision, we can use the Bnd print command again with the -usedby
+option:
$ bnd print -usedby org.jdom-1.1.2.jar
+[USEDBY]
+java.sql org.jdom
+javax.xml.parsers org.jdom.adapters
+...
+org.jaxen org.jdom.xpath
+org.jaxen.jdom org.jdom.xpath
+org.jdom
+ .
+ org.jdom.adapters
+ org.jdom.filter
+...
+This tells us that the Jaxen dependencies (i.e. the +org.jaxen and org.jaxen.jdom packages) are used only from one package in +JDOM, namely org.jdom.xpath. Additionally by looking at the full results +we can see that org.jdom.xpath does not appear on the left hand side, +meaning that it is not imported by any other part of the JDOM library.
+If we simply make our Jaxen imports optional, then a client that imports
+org.jdom.xpath from the JDOM bundle will get NoClassDefFoundError or
+ClassNotFoundException when it tries to use the XPath features. In
+this case it is better to separate org.jdom.xpath into a new bundle.
+Once separated, any client that explicitly needs the XPath features will
+not resolve when the Jaxen bundle offering those features is
+unavailable, which is the desired outcome: it is better to get a
+resolution error than a runtime exception. Separation works in this case
+because the org.jdom.xpath package has good coherency (i.e., it does
+just one thing) and there are no references to it from elsewhere in
+JDOM. If there were such references then the two bundles would be
+tightly coupled to each other and the separation would be pointless.
In order to separate the bundles, we first need to omit org.jdom.xpath
+from the exports of our main JDOM bundle. This is done by refining the
+Export-Package statement as follows:
Export-Package: !org.jdom.xpath,\ *;version=${ver}
+The leading exclamation mark can be read as "not" and it simply excludes +the named package from the generated bundle. Alternative we can just +list each package explicitly, though this requires us to repeat the +version directive on each line:
+Export-Package: org.jdom;version=${ver},\
+ org.jdom.adapters;version=${ver},\
+ org.jdom.filter;version=${ver},\
+ org.jdom.input;version=${ver},\
+ org.jdom.output;version=${ver},\
+ org.jdom.transform;version=${ver}
+We will also need a Bnd descriptor named jdom.xpath.bnd to generate the +JDOM XPath bundle. This is based on our original recipe:
+-classpath: jdom.jar
+Bundle-SymbolicName: org.jdom.xpath
+ver: 1.1.2
+-output: ${bsn}-${ver}.jar
+Bundle-Version: ${ver}
+Export-Package: org.jdom.xpath;version=${ver}
+The previous used-by analysis yielded the package from which an import +dependency resulted. Sometimes we need to dig deeper and find the +individual class(es) responsible for the dependency. Unfortunately this +feature is not available from the Bnd command line, instead we have to +use a macro inside the descriptor file. To find out the class-level +causes for the dependency on the oracle.xml.parser package, add the +following temporary header to the descriptor:
+TEMP: ${classes;IMPORTING;oracle.xml.parser}
This will result in a TEMP header being added to the manifest of the +output bundle. To view it, use Bnd’s print command again with the +-manifest option:
+$ bnd print -manifest org.jdom-1.1.2.jar
+[MANIFEST org.jdom-1.1.2.jar]
+...
+TEMP org.jdom.adapters.OracleV1DOMAdapter
+...
+The output shows that the Oracle parser is used only from a single
+class, strongly supporting our opinion that it is an optional
+dependency. Unfortunately we cannot separate it into its own bundle
+because there are several other such adapters in the same package, each
+with their own dependencies. Performing the same analysis on the Oracle
+parser V2 and Xerces dependencies yields similar results: individual
+classes in the org.jdom.adapters package. This is poor modular design
+on the part of JDOM, but we cannot fix it without breaking existing
+clients. Therefore marking the dependencies as optional is the best
+solution.
Even deeper analysis is sometimes required: e.g., do any other classes
+in JDOM refer to OracleV1DOMAdapter? Such questions are best answered
+by searching the source code of the library. When source code is not
+available then disassembly with the javap tool or decompilation with JAD
+usually helps.
After performing class level used-by analysis, remember to remove any +temporary headers, i.e. TEMP.
+Undesirable imports can also result from "dead code", i.e. code that is
+not reachable from any part of the public API of the library. For
+example libraries sometimes contain JUnit test cases or old classes that
+are no longer used. JDOM does not suffer from such problems, but let us
+suppose for a moment that it did contain an invalid dependency on JUnit.
+We could completely remove the imported org.junit package by adding an
+exclusion rule to Import-Package:
Import-Package: \
+ oracle.xml.parser*;resolution:=optional,\
+ org.apache.xerces.*;resolution:=optional,\
+ !org.junit*,\
+ *
+Exclusion rules should be used with caution, as they can cause the +bundle to produce NoClassDefFoundError. Careful used-by analysis should +be performed to ensure that the dependency really is only relevant to +unreachable code.
+In this example bnd file, we do not provide the dependency JARs, bnd therefore does not +have the ability to automatically calculate the correct version of an +import because the target package is usually not visible when +the bundle is created. Therefore bnd generates imports that use the +default import version range, which in OSGi is implicitly "[0, ∞)", or +"zero to infinity" —in other words, a range that matches any version.
+When such a wide version range is used, the bundle will normally work
+initially, but will suffer problems when the API of the imported package
+evolves. For example, a major new version of the imported API may
+introduce breaking changes, but our bundle will still resolve against
+it. This could result in errors such as IncompatibleClassChangeError,
+AbstractMethodError, NoSuchMethodError, etc. Therefore we should
+manually add import ranges where possible.
For example the JDOM version we are wrapping has been built against
+version 1.1.2. The Jaxen imports can be refined by adding the following
+Import-Package statement to jdom.xpath.bnd:
Import-Package: \
+ org.jaxen.*;version="[1.1,2)",\
+ *
+Note the import range 1.1 through 2, exclusive of 2 — this is in +compliance with OSGi Semantic Versioning guidelines. The +API library may not follow the OSGi guidelines so sometimes an +alternative range may be required.
+Often our biggest problem is working out which version of a dependency +library was used to build the library we are wrapping. In the case of +JDOM, we can find jaxen.jar in the lib directory of its source project +and note that its manifest indicates version 1.1.2. If the project is +built with Apache Maven we can usually find a version in the POM. Other +times we must resort to reading project documentation, if it exists.
+Note that version ranges cannot be added for JRE packages, e.g.
+javax.swing or org.xml.sax because the Java specifications do not
+define the version of any of these packages, and therefore the OSGi
+framework exports them all as version 0.0.0. As an alternative, add a
+Bundle-RequiredExecutionEnvironment header to indicate the basic Java
+level required by the bundle:
Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Other possible values include JavaSE-1.6, OSGi/Minimum-1.0, etc.
+Bnd discovers package dependencies in a bundle by scanning the bytecode
+of the compiled Java files. This process finds all of the static
+dependencies of the Java code, but it does not discover dynamic
+dependencies, for example those arising from the use of
+Class.forName(). There is no generic way for Bnd to calculate all
+dynamic dependencies. However there are certain well-known configuration
+formats that result in dynamic dependencies, and Bnd can analyse these
+formats through the use of plugins.
For example, some bundles use the Spring Framework for dependency +injection. Spring uses XML files that refer to fully qualified Java +class names:
+<bean id="myBean" class="org.example.beans.MyBean">
+</bean>
+Here the org.example.beans package is a dependency of the bundle that
+should be added to Import-Package. Bnd can discover this dependency by
+adding a Spring analyser plugin via a declaration in the descriptor
+file:
-plugin: aQute.lib.spring.SpringComponent
+Similar plugins exist for JPA and Hibernate, and custom plugins can be +written to support other configuration formats or scripting languages.
+In summary the process of wrapping a JAR as an OSGi bundle is as +follows:
+Create the bnd descriptor, using the template in Appendix A.
+Generate the initial wrapping and review the imports for suspicious +dependencies.
+Fix problematic dependencies using the following heuristics:
+if the dependency arises from a package that is coherent, has an + essential need for the dependency, and is not referred from other + parts of the bundle, separate this package into its own bundle;
+otherwise the dependency is probably valid and necessary, so should + be left in the bundle.
+Add import version ranges for non-JRE imports, where possible.
+The following template bnd descriptor can be used for the initial +wrapping. The placeholders on the first three lines must be filled in:
+-classpath: <INPUT JAR(S)>
+Bundle-SymbolicName: <NAME>
+ver: <VERSION>
+-output: ${bsn}-${ver}.jar
+Bundle-Version: ${ver}
+Export-Package: *;version=${ver}
+# Uncomment next line to customize imports. The last entry MUST be "*"
+# Import-Package: *
+Bnd can help you to maintain your projects' documentation up-to-date. +In a best effort, Bnd can automatically update your documentation according to changes in your programs. +Whenever it is not possible, you still have an easy way to edit the documentation manually.
+The process is in two steps: first, Bnd will analyze your projects and your bundles to
+collect and aggragate relevant data into an intermediate format (json or xml). Then,
+it will process the data with a template file (xslt or twig) to generate the documentation.
Relevant data are the OSGi headers, the configuration of your services, code snippets that you write +with the help of your IDE, your Gogo commands, etc. Once they are collected you have three ways to use them:
+This feature is available for the Bnd Workspace Model and Maven projects respectivly using Bnd CLI and bnd-reporter-maven-plugin. We will use
+Bnd CLI as an example in the next sections, you can directly look at the documentation in the Github repository for the corresponding feature with Maven.
++Note: It is necessary that the workspace is completely built before generating the documentation files to take into account the latest changes.
+
Custom documentations are configured with the -exportreport instruction. +This instruction defines a list of reports which can then be exported with the exportreport export subcommand.
+For example, executing bnd exportreport export on a project with the following configuration:
bnd.bnd
+-exportreport: metadata.json, info.html;template=https://.../mytemplate.xslt
+will export two files: metadata.json (intermediate data) and info.html (final documentation file).
With the exportreport readme subcommand you can generate a set of README files.
+If this command is applied on a workspace, a readme.md file will be generated into the workspace folder as well as one
+readme.md file for each project. If a project builds multiple bundles, additional readme.<bsn>.md will be generated for each
+bundle built.
In case you want to customize the README files with your own text, you have to create a readme.twig file next to the readme.md file that you wish to customize.
The simplest way to proceed is to copy-paste the following snippet into this file:
+Write your own markdown text here.
+Inside an embed tag one can only specify block tags which will override the parent template included.
+Here, the parent template is the readme template file built-in into Bnd available at the default:readme.twig URL (see the file here). This file defines a set of blocks that you can override with your own text.
+In the above snippet, we override the beforeTitle block. You can add multiple blocks depending on where you want to insert your text, here is a list of available blocks:
beforeTitle, afterTitlebeforeOverview, afterOverviewbeforeLinks, afterLinksbeforeCoordinates, afterCoordinatesbeforeArtifacts, afterArtifactsbeforeCodeUsage, afterCodeUsagebeforeComponents, afterComponentsbeforeDevelopers, afterDevelopersbeforeLicenses, afterLicensesbeforeCopyright, afterCopyrightbeforeVendor, afterVendorbefore* blocks are shown between the title of a section and the generated content. after* blocks are shown at the end of a section, after the generated content.
| Name | +Description | +Default value | +
|---|---|---|
iconUrl |
+URL to an icon that will be shown in front of the title | +"" | +
The command line interface (bnd CLI) provides various tools to invoke bnd functions and features. It is using bndlib under the hood just like other tooling like bndtools, maven or gradle plugins do too.
+Checkout the tutorial which makes use of the CLI.
+See here to install the CLI.
+The bnd CLI can be invoked in several different ways:
+In this text bnd is used as if it is a command line program. This should be set up as:
java -jar
||General Option ||Description || +||--debug ||Show log debug output|| +||--failok ||Turns errors into warnings so command always succeeds || +||--exceptions ||Will print the exception when the software has ran into a bad exception and bails out. Normally only a message is printed. For debugging or diagnostic reasons, the exception stack trace can be very helpful. ||
+Show list of all options for the bnd CLI.
+It is quite easy to use bnd from Java, you only need to include biz.aQute.bndlib on your class path. This chapter shows you some samples of how to use bndlib.
+By default, bnd creates a container with resources and then calculates the manifest. However, these phases are separated although they use the same instructions. The following snippet therefore shows how you can create a manifest from an existing file or directory.
+Analyzer analyzer = new Analyzer();
+Jar bin = new Jar( new File("bin") ); // where our data is
+analyzer.setJar( bin ); // give bnd the contents
+
+// You can provide additional class path entries to allow
+// bnd to pickup export version from the packageinfo file,
+// Version annotation, or their manifests.
+analyzer.addClasspath( new File("jar/spring.jar") );
+
+analyzer.setProperty("Bundle-SymbolicName","org.osgi.core");
+analyzer.setProperty("Export-Package",
+ "org.osgi.framework,org.osgi.service.event");
+analyzer.setProperty("Bundle-Version","1.0");
+
+// There are no good defaults so make sure you set the
+// Import-Package
+analyzer.setProperty("Import-Package","*");
+
+// Calculate the manifest
+Manifest manifest = analyzer.calcManifest();
+
+public interface MakePlugin {
+/**
+ * This plugin is called when Include-Resource detects
+ * a reference to a resource that it can not find in the
+ * file system.
+ *
+ * @param builder The current builder
+ * @param source The source string (i.e. the place
+ * where bnd looked)
+ * @param arguments Any arguments on the clause in
+ * Include-Resource
+ * @return A resource or null if no resource
+ * could be made
+ * @throws Exception
+ */
+Resource make(Builder builder, String source,
+ Map<String,String> arguments) throws Exception;
+}
+Make plugins kick in when the Include-Resource header tries to locate a resource but it cannot find that resource. The -make option defines a number of patterns that are mapped to a make instruction.
For example, if you have
+Include-Resource: com.acme.Abc.ann
+If no such resource is found, bnd will look in the -make instruction. This instruction associates a pattern with a plugin type. For example:
+-make: (*.jar); type=xyz; abc=3; def="$1"
+The first name part of the clause is matched against the unfound resource. All plugins are called sequentially until one returns non-null. The arguments on the -make clause are given as parameters to the make plugin. Normally all Make Plugins should verify the type field.
+bnd has a bnd and a copy Make Plugin.
+If, for example, you created a library for bnd and want to make it easy for the user to setup his functionality have a look at template fragments.
+ + diff --git a/org.bndtools.help/docs/chapters/620-template-fragments.html b/org.bndtools.help/docs/chapters/620-template-fragments.html new file mode 100644 index 0000000000..f706a15d34 --- /dev/null +++ b/org.bndtools.help/docs/chapters/620-template-fragments.html @@ -0,0 +1,129 @@ + + + + +A good workspace setup makes all the difference in the development cycle. Since a workspace can +contain many projects, creating new workspaces is relatively rare. However, a workspace tends +to consist of many aspects. There is the gradle setup, the OSGi release, maybe the maven +layout etc. Initially, we attempted to use a single GitHub repository as a template for new workspaces, but this approach quickly became complex.
+For example, if someone created a library for bnd, to make it easy for the +user to setup his functionality, the author of the library had to maintain a full workspace +with gradle, etc. This put the burden on chasing the OSGi release, the gradle releases, on the +people that were willing to spread the gospel around OSGi/bndtools.
+Instead, we developed fragment templates. A fragment models one aspect of a workspace and is maintained in Github repository, but one repository can hold many fragments. Multiple template fragments can be combined by the end-user to enrich the workspace with various aspects. +During workspace creation there is a wizard where one can select template fragments via checkboxes.
+Official templates from the bndtools Github organisation are shown with a green checkmark. +Non-official templates from 3rd-party authors are shown with a yellow exclamation mark.
+++Note: Check templates before installation
+Make sure you carefully check templates beforehand, since this is content from potentially untrusted sources which is downloaded to your computer. +You should double click on an entry to open the Github repostory of that template in your browser. +This allows you to examine the content which is about to be downloaded to your workspace when you press finish. If you select at least one 3rd-Party template a confirmatin popup asks you to confirm, if you trust the authors. +{: .block-warning }
+
The workspace is already prepared for this model of fragments. The merged instructions mean that
+we can extend the properties and instructions from many different sources. However, the most important
+feature here is the cnf/ext folder. Any bnd or special fragment file placed in this folder will automatically
+be ready before the build.bnd file is read. For example, a fragment could contain the
+index for OSGi R8. See build.
There is a single master index for all template fragments hosted on
+https://github.com/bndtools/workspace-templates/blob/master/index.bnd
+This index is open for any person or organization that maintains one or more fragment and +seeks an easy way to make them available to bndtools users. All that is needed is to host the fragment somewhere and provide +a pull request (PR) to add it to this index file.
+The format of the index file is like all Parameters.
+key – The key is the identity of the fragment. It is either a URL or provides the following structure:
id ::= [organization ['/' repository [ '/' path ] ['#' git-ref ]]
The id is resolved against bndtools/workspace-templates#master. Therefore, in the example organization,
+the example is a valid id that resolves to example/workspace-templates#master. Since the ID has a path,
+it is possible to host multiple fragments in a single repository.
+Instead of a branch name like #master as git-ref you could also point to a specific commit via its SHA e.g. #567648ff425693b27b191bd38ace7c9c10539c2d.
The index has the following attributes for a clause:
+name – The human readable name for the template fragmentdescription – A human readable description for the template fragmentrequire – A comma separated list of fragment ids. Do not forget to quote when multiple fragments are requiredtag – A comma separated list of tags, quotes are needed when there are multiple.For example consider this index.bnd:
-workspace-templates \
+ bndtools/workspace-templates/gradle; \
+ name=gradle; \
+ description="Setup gradle build for bnd workspace", \
+ bndtools/workspace-templates/maven#master; \
+ name=maven; \
+ description="Use the maven directory layout for sources and binaries", \
+ bndtools/workspace-templates/osgi#master; \
+ name=osgi; \
+ description="OSGi R8 with Felix distribution",\
+ acme-org/myrepo/subfolder/workspace-template#commitSHA; \
+ name=3rdparty-template; \
+ description="A Template fragment by acme-org at a specific commit SHA"
+The id must resolve to a folder in the repository, and can be translated to github URLs:
+| ID | +URL | +
|---|---|
bndtools/workspace-templates/gradle |
+https://github.com/bndtools/workspace-templates/tree/master/gradle |
+
bndtools/workspace-templates/maven#master |
+https://github.com/bndtools/workspace-templates/tree/master/maven |
+
acme-org/myrepo/subfolder/workspace-template#otherbranch |
+https://github.com/acme-org/myrepo/tree/otherbranch/subfolder/workspace-template |
+
acme-org/myrepo/subfolder/workspace-template#567648ff425693b27b191bd38ace7c9c10539c2d |
+https://github.com/acme-org/myrepo/tree/567648ff425693b27b191bd38ace7c9c10539c2d/subfolder/workspace-template |
+
By default, bnd will recursively copy the
+content to the new workspace. However, if there is a file tool.bnd present it will use this to
+guide the copying process. This bnd file looks like:
-tool \
+ .*;tool.bnd;readme.md;skip=true, \
+ gradle.properties;macro=true;append=true,\
+ gradlew;exec=true, \
+ *
+The -tool instruction is a SELECT that is matched against the file paths (not names!) in the fragment template folder.
+The attributes that can be set are:
skip=true – Skip the selected file,append=true – Allow duplicates, appendexec=true – Set the execute bit on non-windows systemspreprocess=true – Preprocess the file (this uses the tool file and the current workspace as domain.delete=true – Deletes an existing file in the target. Cannot be wildcardedIt is possible to define the -workspace-templates instruction in the build.bnd file.
+It must contain the same format as the index. This local index is merged with the main index. In the
Bndtools GUI, it is possible to add an additional index from a URL or file via the '+' button.
+For example you can directly point to a Github URL like:
+https://raw.githubusercontent.com/bndtools/workspace-templates/567648ff425693b27b191bd38ace7c9c10539c2d/index.bnd
Bnd and Bndtools runs on any system that can run Eclipse. However, every operating system is different and has its own quirks. Windows in particular has a few that impact developers using Bnd. While the Bnd experience may never be as smooth on Windows as it is on macOS or Linux, there are a few tweaks that make it a lot smoother than what comes out of the box.
+It is possible that some of the tips listed here will also help for Eclipse development work in general, or perhaps any development work on Windows.
+Windows has two key architectural differences to *nix-based OSes that really impact development work in Java:
+Most *nix systems use advisory file locking, rather than mandatory locking. Windows uses mandatory locking rather than advisory. Most Java programs are developed on *nix and tend to assume *nix locking semantics. This means that they are not as careful at cleaning up after themselves and ensuring that file locks are released.
+In particular, there are problems when an application exits using System.exit() - the normal way for Java applications to clean up is to release a lock is in a finally clause. However, when an application calls System.exit(), these normal channels are bypassed, which means the JVM never gets a chance to release the locks and hence can't delete the locked files.
From version 10, Windows' built-in anti-malware program Windows Defender has become very tightly integrated with the OS - seemingly, it is written into the Windows kernel API IO functions themselves, which means it's nearly impossible to bypass. This makes it extremely well suited to catching malware in the act. Unfortunately, it means it slows down every file write. When you are writing lots of small files, this delay is significant.
+Prior to Bnd 5.0, there was a bug that caused the launcher to pause for several seconds if you had -runtrace enabled. This bug has been fixed as of 5.0 (currently in pre-release at the time of writing), so it is worthwhile upgrading to use 5.0 if you are able.
Due to file locking issues, Bnd on Windows will always copy bundles into a newly launched application rather than referencing them (see -runnoreferences). This copy can run to a few hundred megabytes.
Again due to file locking (and its overuse of System.exit()), Bnd is not very good at cleaning up temporary files on Windows after it exits. So usually, the application folders are left behind after the launched application exits.
A few hundred megabytes per launch starts to add up, and if you're running off a relatively small SSD you can find yourself running out of disk space.
+It is recommended that you periodically clear out these files from your temp directory. By default, the temp directory is in %UserHome%\AppData\Local\Temp. OSGi temp dirs created by Felix typically have the pattern: osgi.nnnnnnnnnnnnnnnnn.fw. Depending on which OSGi framework and which Java utilities you are using in your app, there may be others to look out for - if you are using a custom temp dir (see below), then you can simply clear out the entire directory.
You can turn off real-time malware protection in Windows settings. However, Windows 10 will only allow this situation to persist for a short time (perhaps 24 hours) before automatically turning it back on again. Also, this approach leaves your entire system without any real-time protection.
+A better solution is to disable antivirus scanning for specific directories. You can find instructions on how to do this here. For maximum benefit, you should exclude:
+* your Bnd workspace;
+* the corresponding Eclipse workspace; and
+* your temp directory (by default: %UserHome%\AppData\Local\Temp).
+However, excluding your temp directory might be a bit of a security hole as it is likely to be a place where malware will try and write first. If you are concerned about this, see the next section
+for a workaround.
It is possible that excluding other directories may help performance, as Windows Defender
+seems to take a keen interest in jar files. Some examples:
+* your .m2 directory (usually found in your home directory);
+* the P2 repository for your local Eclipse installation. This is either in your Eclipse
+ root directory, or else in %UserHome%\.p2 directory if you have installed it using
+ the P2 bundle pool.
+* any other local repositories you might be using in your workspace (that are not already
+ covered by one of the above exclusions).
Be wary that each additional exclusion you add increases the security risk. You need to weigh the performance benefits of excluding each folder vs the increase in security risk.
+As noted above, for best performance you should exclude the temp directory, however for best safety you may not wish to. A good compromise is:
+C:\temp).-Djava.io.tmpdir=<path to tmp> vm arg to the eclipse.ini file..bndrun files to use this temp directory by using the -runvm instruction (eg: -runvm: -Djava.io.tmpdir=C:\\temp - noting the double-backslash).As an added bonus, this makes cleaning up the remaining temp files much easier as you can safely delete the entire directory, knowing that no other applications are using the directory.
+If you've got an SSD, the above tips should suffice to get good performance. On one test, launching the Bndtools test Eclipse instance itself (with over 200 bundles) took ~2s to install all the bundles for a running Eclipse instance.
+However, if you're using a slower hard disk and you have enough RAM (at least 8GB), you may also consider configuring a small RAM disk of around 1GB and putting your temp directory on the RAM disk. There are a few free RAM disk utilities available for Windows.
+You can also easily clear out the accumulated temp files by unmounting and re-mounting the RAM disk.
+ + diff --git a/org.bndtools.help/docs/chapters/700-tools.html b/org.bndtools.help/docs/chapters/700-tools.html new file mode 100644 index 0000000000..5884dce52b --- /dev/null +++ b/org.bndtools.help/docs/chapters/700-tools.html @@ -0,0 +1,22 @@ + + + + +If you're a developer of a tool that needs to be listed here, do not hesitate to submit a pull request at github.
+ + diff --git a/org.bndtools.help/docs/chapters/790-reference.html b/org.bndtools.help/docs/chapters/790-reference.html new file mode 100644 index 0000000000..67a847deca --- /dev/null +++ b/org.bndtools.help/docs/chapters/790-reference.html @@ -0,0 +1,33 @@ + + + + +The subsequent sections provide the reference part of the manual. This consists of the following:
+- (minus sign).Since bnd is a library, it gets used in many different places. This makes some of the headers, instructions, and/or +macros only applicable in a specific context. This is generally indicated on a page with a class button on the right +side. For example:
+The following classes are used:
+Usually the format of both headers and instructions follows the Java properties files specification outlined in Concepts.
+ + diff --git a/org.bndtools.help/docs/chapters/800-headers.html b/org.bndtools.help/docs/chapters/800-headers.html new file mode 100644 index 0000000000..90b47888c6 --- /dev/null +++ b/org.bndtools.help/docs/chapters/800-headers.html @@ -0,0 +1,37 @@ + + + + +Learn the basics of bnd headers on the Generating JARs page.
+ + + diff --git a/org.bndtools.help/docs/chapters/820-instructions.html b/org.bndtools.help/docs/chapters/820-instructions.html new file mode 100644 index 0000000000..508dfb7ca2 --- /dev/null +++ b/org.bndtools.help/docs/chapters/820-instructions.html @@ -0,0 +1,366 @@ + + + + +A bnd instruction is a property that starts with a minus sign ('-'). An instruction instructs bndlib to do something, in general providing parameters to the code. All instructions in bndlib are listed later in this page.
+All instructions are listed on the Instruction index
+Almost all bndlib instructions follow the general OSGi syntax. However, the bndlib syntax is in general a bit more relaxed, for example you can use either single quotes (''') or double quotes ('"') while OSGi only allows double quotes. Dangling comma's and some other not exactly correct headers are accepted by bndlib without complaining. Values without quotes are accepted as long as they cannot confuse the general syntax. For example, a value like 1.20 does not have to be quoted since the period ('.') cannot confuse the parser on that place, however, a value like [1.3,4) must be quoted since it contains a comma.
+In this mannual we adhere to the same conventions as the OSGi specification. This is mostly reflected in the names for the terminals in the syntax specifications. As a reminder we repeat the syntax rules and common terminals:
+* – Repetition of the previous element zero or more times, e.g. ( ',' element ) *+ – Repetition one or more times? – Previous element is optional( ... ) – Grouping| – Or[...] – Set (one of).. – Range<...> – Externally defined token, followed with a description~ – Not, negationThe following terminals are pre-defined:
+ws ::= <see Character.isWhitespace>
+digit ::= [0..9]
+alpha ::= [a..zA..Z]
+alphanum ::= alpha | digit
+token ::= ( alphanum | '_' | '-' )+
+number ::= digit+
+jletter ::= <see [JSL][1] for JavaLetter>
+jletterordigit ::= <See [JLS][1] for JavaLetterOrDigit >
+qname ::= <See [JLS][1] for fully qualified class name >
+identifier ::= jletter jletterordigit *
+extended ::= ( alphanum | '_' | '-' | '.' )+
+quoted-string-d ::= '"' ( ~["\#x0D#x0A#x00] | '\"'|'\')* '"'
+quoted-string-s ::= ''' ( ~['\#x0D#x0A#x00] | '\''|'\')* '''
+quoted-string ::= quoted-string-s | quoted-string-d
+special-chars ::= ~["\#x0D#x0A#x00:=;,<See [JLS][1] for whitespace>]
+path ::= special-chars+ | quoted-string
+value ::= quoted-string | path
+argument ::= extended | quoted-string
+parameter ::= directive | attribute
+directive ::= extended ':=' argument
+attribute ::= extended '=' argument
+parameters ::= parameter ( ',' parameter ) *
+unique-name ::= identifier ( '.' identifier )*
+symbolic-name ::= token('.'token)*
+package-name ::= unique-name
+
+version ::= major( '.' minor ( '.' micro ( '.' qualifier )? )? )?
+major ::= number
+minor ::= number
+micro ::= number
+qualifier ::= ( alphanum | '_' | '-' )+
+
+version-range ::= interval | atleast
+interval ::= ( '[' | '(' ) floor ',' ceiling ( ']' | ')' )
+atleast ::= version
+floor ::= version
+ceiling ::= version
+White spaces between terminals are ignored unless specifically noted. The only exception is the directive, a directive must be connected to the colon (':'). That is, bndlib stores attributes and directives in the same map and distinguishes them by the fact that directives end in a colon and attributes do not. For example, using a directive like foo; path := https://www.xyz.com will not work correctly because the path : is not a valid attribute nor directive name. Any value that contains a space, a tab, a comma, colon, semicolon, equal sign or any other character that is part of a terminal in the grammar must be quoted.
Almost all bndlib instructions follow the general OSGi syntax for a header.
+-instruction ::= parameters
+parameters ::= clause ( ',' clause ) *
+clause ::= path ( ';' path ) *
+ ( ';' parameter ) *
+There are a number of short-cuts that are regularly used in bnd commands:
+list ::= value ( ',' value ) *
+url ::= <URL>
+file-path ::= <file path, using / as separator>
+Most instructions that accept multiple clauses are merged instructions. A merged instruction is an instruction which is specified by its prefix but which will merge any property that starts with this prefix followed by a period ('.'). For example, the base instruction -plugin instruction accepts -plugin.git as well. The reason for this merging is that allows to append the base instruction with instructions from other files. For example, the bnd file in the cnf/ext directory are automatically included in the properties of the workspace. Since there can be many files, there would be a need to coordinate the using of a singleton base instruction. As always, singletons suck and in this case we solved it with merged instructions.
Some instructions put, sometimes very subtle, semantics on their ordering. To prevent different results from run to run, we make order the merge properties by placing the base instruction in front and then followed by the values from the other keys in lexically sorted order. For example, -a, -a.x, and -a.Z, and -a.1 will concatenate the clauses in the order -a,-a.1,-a.Z,-a.x.
Each of the constituents of the merged properties can be empty, this is properly handled by bndlib and will not create a an empty clause as would easily happen when the same thing was done with macros. Empty clauses can help when there is an optionality that depends on some condition.
+As an example, lets set the -buildpath instruction:
-buildpath: com.example.foo;version=1.2
+
+-buildpath.extra: ${if;${debug};com.example.foo.debug\;version=1.2}
+This will result in a buildpath of (when debug is not false) of: com.example.foo;version=1.2, com.example.foo.debug;version=1.2.
Instructions can also be decorated. A decorator is a header that ends with + or ++. A header like -runbundles is first merged and then decorated.
In this case, -runbundles is the stem. First, the total header is assembled by merging the property that has that stem. If there are properties that match stem + +.* or ++.*, then these properties are used to decorate the merged property. Notice that for the decorator the root key includes the + sign, the suffixes must come after the + sign. For example, for the header foo, the decorator would be foo+ and that would match a key like foo+.bar.
The decorator is a Parameters, it consists of a key and a set of attributes. The decorator key is usually a glob expressions.
+After the header is merged, the key of each entry is matched against all globs in the decorator following the order of the decorator. When the first match is found, the attributes of the decorator clause that matches are stored with the attributes of the Parameter entry, overriding any attribute with the same attribute key. A Parameter entry key can only match one decorator glob expression.
+For example, -runbundles a and -runbundles+ *;startlevel=20 will result in the content a;startlevel=20.
If the name of the decorator clause attribute starts with !, then the attribute, using the attribute name after removing the leading !, is removed from the Parameter entry. If the name of the decorator clause attribute starts with ~, then the decorator clause attribute value will not overwrite an existing value of the Parameter entry attribute, using the attribute name after removing the leading ~.
Example:
+ -foo a, b, c;skip=true, d
+ -foo+ b;skip=true,c;skip=false
+ -foo+.d d;skip=true
+In this case, the first entry matched is b and it is matched against the second entry in the -foo instruction. Since the decorator has the skip=true attribute, it is carried over to the instruction. The result is therefore:
a, b;skip=true; c;skip=false, d;skip=true
+If the decoration ends with 2 plus signs, for example -foo++, then the literals in the decoration headers will be added to the result if they are not matched to any key in the source header. If the decoration ends with a single + sign, literals that do not match are ignored.
decorated that can be used to apply decoration to any property keyIf a value in an instruction has a scope then you will be able to use a selector. A scope is a well defined, finite, set. For example, if you use the '-exportcontents' instruction you can specify the packages that can be exported. Since you can only export the packages that are inside your bundle, the scope is the set of package names that were collected so far.
+The syntax for a selector is:
+selector ::= '!' ?
+ '=' ?
+ '*' | ( '.' | '?' | '|' | '$' | . | other ) *
+ ( '.*' ) ?
+ ( ':i' | ':o' | ':io' ) ?
+A selector is an expression that is matched against all members of the scope. For example, com.example.* will match any package name that starts with com.example, for example com.example.foo, as well as com.example itself. The syntax used to describe the wildcarding is based on a globbing kind model, however, the selector is translated into a regular expression according to some simple rules.
The replacement rules to build the regular expression are as follows:
+* – Is replaced with .*, matching any number of characters, triggers wildcard match.? - Is replaced with .?, matching any character, triggers wildcard match.| – Is inserted as is, triggers wildcard match.$ – Escaped to \$.. – The dot is treated as a package segment separator. It is escaped with \.. However, if the match ends with .*, the replacement will be (\..*)?. This triggers a wildcard match.If you want to go ballistic with regular expressions, then you can go ahead. As long as the wildcards are triggered by one of the defined characters, the replaced string will be used as a regular expression.
+If the selector ends with :i then a case insensitive match is requested, this will ignore any case differences.
+If the selector ends with :o then the selector is marked optional. This means that when Bnd might warn about unused selectors, it will not warn that an optional selector is unused.
+You can specify both flags by ending the selector with :io.
A selector can be prefixed with an exclamation mark, this indicates that a match must be treated as a removal operation. To understand removal, it is necessary to realize that the selectors are not declarative, the order of the selectors is relevant.
+When bndlib has a scope, it will iterator over each element in the scope and then for element it will iterator over each selector. If there is a match on the scope member then it is always removed from the scope, it will not be matched against later selectors. If the selector starts with an exclamation mark then the member is removed. but it is not treated as a result. Ergo, the exclamation mark allows you to remove members before they are actually matched.
+For example, you want to match all implementation packages (i.e. they have impl in their name) except when they start with com.example. This can be achieved with:
!com.example.*impl*, *.impl.*
+Last, and also least, you can prefix the selector with an equal sign ('=') (after the exclamation mark if one is present). This will signal a literal match with the string, no globbing will be done.
+The way the period ('.') is treated is kind of special. The reason is that people expect that the selector com.example.* actually includes the package com.example itself. In bndlib this has always been the case although the OSGi Alliance later decided to not follow this pattern when there was a need for the globbing of packages.
In this example, the first selector will remove any match and ignore it, the second selector now only sees scope members that are fitting in the earlier pattern.
+Selectors are used in virtually any place where there is a reasonable scope. It is a very powerful feature. However, it is also easy to go overboard. If you find you need to use this feature excessively then you are likely overdoing it.
+One of the most painful thing in making bndlib run anywhere is handling of files. Even though Java has a decent abstraction of the file system, problems with spaces in file names and backslashes in the wrong place have caused an uneven amount of bugs. Ok, this partially because most bndlib developers tend to use Macs, still no excuse and quite embarrassing.
+Since bnd files need to be portable across environments we've chosen to use the Unix file separator, the slash (or solidus '/') for more reasons than I can reasonably sum up here (what was Bill smoking when he picked the reverse solidus for file separator!). In bndlib, all file paths (ok, should) always go through a single method that actually parses it and turns it into a Java File object. This allows us to support a number of features in a portable way. The syntax of a file path is therefore:
+file ::= ( '~/' | '/' )? ( ~['/']+ '/' ) * ~['/'] *
+If a file path starts with:
+You can use the '..' operator for a segment in the file path to indicate a parent directory. It is highly advised to always use relative addressing to keep your workspace portable. However, there are a number of macros that can be used as anchors:
+${p}, `${project} – The directory of the current project${workspace} – The directory of the current workspace${build} – The directory of thecnf` directory.A FILESPEC defines a set of files relative to a starting point. It can be identical to a FILE for a single file but it has some special syntax to recurse directories. For example, the FILESPEC foo/bar/**/spec/*.ts finds all files that end in spec/*.ts somewhere below the foo/bar directory.
The syntax is as follows:
+FILESPEC ::= filespec ( ',' filespec )*
+filespec ::= ( segment '/' ) filematch
+segment ::= '**' | GLOB
+filematch ::= '**' | '**' GLOB | GLOB
+If a segment is ** then it will match any directory to any depth. Otherwise it must be a GLOB expression, which can also be a literal. The last segment is the filematch. This is a GLOB expression on a file name. As a convenience, if it is **, any file will match in any directory and if it starts with something like **.ts then it will also recurse and match on *.ts. That is, the following rules apply to the filematch:
prefix/** prefix/**/*
+prefix/**.ts prefix/**/*.ts
+A PATH is a repository specification. It is a specification for a number of JARs and/or bundles from a repository (well, most of the time). A PATH has the following syntax:
+PATH ::= target ( ',' target ) *
+target ::= ( entry | FILE ';' 'version' '=' 'file' )
+ ( ';' PARAMETERS ) *
+entry ::= symbolic-name ( ';' version ) ?
+version ::= 'version' '='
+ ( RANGE | 'latest' | 'project')
+A PATH defines a number of bundles with its entries. Each entry is either a FILE or a symbolic-name. If only a symbolic name is specified, the default will be the import range from 0.0.0. This selects the whole repository for the given symbolic name. Outside the default, the repository entry can be specified in the following ways:
+latest – Use the project's output or, if the project does not exists, get the bundle with the highest version from the repositories.project – Mandate the use of the project's outputA glob is a pattern that can be matched against a candidate to see if it matches. To match multiple candidates with
+one pattern, the pattern is allowed to contain wildcard characters. A wildcard character is a stand in for zero or
+more other characters. For example, a glob like *.xml matches any name that ends with .xml, the asterisk * stands
+in for zero or more characters. Therefore, it matches foo.xml, xml.xml.xml, but also just .xml. The other
+wildchar is the question mark (?) which matches exactly one character. For example, ???.xml matches abc.xml but not a.xml
+or .xml.
Sometimes it is necessary to match against a number of strings. The vertical bar (|) can separate these strings. For
+example, abc|def|ghi matches any of abc, def, or ghi.
It is also possible to group strings inside the pattern using parentheses. For example, foo(a|b)bar matches fooabar
+or foobbar.
The glob also supports character classes with the square brackets [ and ]. Characters between the square brackets
+literally match to any character at that position. These character classes also form a group. For example, foo[abc]bar matches
+fooabar, foobbar, or foocbar.
Groups can be repeated using a question mark (cardinality 0..1), the asterisk (cardinality 0..n), or the plus sign (cardinality 1..n). For example, (a)*
+matches aaaa but also the empty string. If the standard cardinalities do not suffice, then a group can be suffixed by a
+cardinality specification using the curly braces ({ and }). Inside the curly braces the lower bound and the optional upper
+bound can be specified. If no upper bound is specified then the lower bound is also the upper bound.
+For example, (?i)([0-9A-F][0-9A-F]){20} matches a 40 digit SHA digest in hex regardless of case.
There are some special characters that get special treatment:
+special ::= '{' | '}' | '|' | '+' | '*' | '?' | '(' | ')' | '[' | ']'
+These characters can be escaped with a reverse solidus (\) to override their special meaning, they will then be matched literally. To
+escape a long string, it is possible to place \Q at the beginning of a block that needs to be escaped and \E at the end.
+For example, foo\Q*****\Ebar
Some examples:
+| Glob | +Regular Expression | +Matches | +
|---|---|---|
abc.ts |
+abc\.ts |
+abc.ts |
+
*.ts |
+.*\.ts |
+abc.ts, def.ts |
+
foo***bar |
+foo.*.*.*bar |
+foobar, fooXbar, fooXXXXbar |
+
foo(A|B|C)*bar |
+foo(A|B|C)*bar |
+fooAbar, fooBBBAbar,fooCbar |
+
foo\{\}bar |
+foo\{\}bar |
+foo{}bar |
+
xx(?i)xx |
+xx(?i)xx |
+xxXx, xxXX, xxxx |
+
Some instruction use the GLOB but provide some convenience. For example, the package selectors of Export-Package et. al.
+detect a case like com.example.* and turn it into a regular expression that matches com.example.foo but also matches
+com.example. This is generally explained in the instruction.
This section defines how globs are mapped to regular expressions. Although the globs are quite intuitive to use, +they do expose all regular expression capabilities. A strong advice is to keep it simple. The mapping itself +is actually non-trivial and takes a number of heuristics that can easily go wrong. Basically, if you need this +section you're likely doing something that is too complex.
+For backward compatibility reasons, we support the unix like glob style for or'ing like {a,b,c}. However, it is
+recommended to use the form using the vertical bar since it is more flexible and closer to regular expressions.
A glob is mapped to a regular expression character by character. During this traversal the following states are kept:
+SIMPLE – Basic simple glob form. This is the default. CURLIES– Inside a curly braces group that is not a cardinality specification. Curly braces can be nested.QUOTED – Inside a \Q...\E block (cannot be nested) BRACKETS – Inside a character group enclosed by square brackets (cannot be nested).Character sets:
+escaped ::= `.` | `$` | `^` | `@` | `%`
+special ::= `?` | `*` | `+`
+end ::= ')' | ']' | '}'
+start ::= '('
+Escaped characters are escaped in SIMPLE mode and CURLIES mode. In QUOTED or BRACKETS mode no escaping is done. For example
+^$ becomes \^\$ matching literally ^$. However, [^$] becomes [^$], matching anything but a dollar sign. In QUOTED
+and BRACKETS mode, also the special, end, and start sets are directly inserted without any special handling.
In the SIMPLE and CURLIES mode the special characters require special handling. For these remaining mappings it is important
+to realize that there are groups. There are parenthesized groups, bracketed group, and curly braces groups. For
+example, (a|b), [abcd], or {a,b,c} respectively. If curly braces are used after a group then it is a quantifier and not considered a group. That is, {a,b} is
+considered a group but [a,b]{1,2} then the {1,2} is a quantifier and not a group.
So when a special character is preceded with a group, it is inserted without any special processing. If it is not
+preceded then the following rules apply:
* – mapped to .*, this matches any number of characters? – mapped to ., this matches one character+ – mapped to \+, matches a plus sign!Some examples:
+| Glob | +Regular Expression | +
|---|---|
* |
+.* |
+
(a)* |
+(a)* |
+
[abc]+ |
+[abc]+ |
+
. |
+\. |
+
[.*+|] |
+[.*+|] |
+
+ |
+\+ |
+
(x|y)+ |
+(x|y)+ |
+
{x,y} |
+(?:x|y) |
+
{[xa],y} |
+(?:[xa]|y) |
+
(?:foo) |
+(?:foo) |
+
{foo} |
+(?:foo) |
+
[\p{Lower}] |
+[\p{Lower}] |
+
[a-z&&[^bc]] |
+[a-z&&[^bc]] |
+
Learn the basics of bnd instructions in the Instruction reference.
+ + + diff --git a/org.bndtools.help/docs/chapters/850-macros.html b/org.bndtools.help/docs/chapters/850-macros.html new file mode 100644 index 0000000000..8c28fc989d --- /dev/null +++ b/org.bndtools.help/docs/chapters/850-macros.html @@ -0,0 +1,76 @@ + + + + +Bnd has a simple macro processor for the header processing. Variables allow a single definition of a value, and the use of derivations. Each header is a macro that can be expanded. Notice that headers that do not start with an upper case character will not be copied to the manifest, so they can be used as working variables. Variables are expanded by enclosing the name of the variable in ${<name>} (curly braces) or $(<name>) (parenthesis). Additionally, square brackets [], angled brackets <>, double guillemets «», and single guillemets ‹› are also allowed for brackets. If brackets are nested, that is $[replace;acaca;a(.*)a;[$1]] will return [cac].
There are also a number of macros that perform basic functions. All these functions have the following basic syntax:
+ macro ::= '${' function '}'
+ | '$\[' function '\]'
+ | '$(' function ')'
+ | '$<' function '>'
+
+ function ::= name ( ';' argument ) *
+For example:
+version=1.23.87.200109111023542
+Bundle-Version= ${version}
+Bundle-Description= This bundle has version ${version}
+All macros are listed on the Macro index
+The default macro pattern is the ${...} pattern, a dollar sign ('$') followed by a left curly bracket ('{') and closed by a right curly bracket ('}'). However, since bndlib is often used inside other systems it also supports alternative macro patterns:
$(...), $<...>, $[...], $«..» (pointing double angle quotation mark \u00AB abd \u00BB), and$‹...› (single pointing angle quotation mark)@Since("2.3") Macros can contain arguments. These arguments are available in the expansion as ${0} to ${9}. ${0} is the name of the macro, so the actual arguments start at 1. The name is also available as ${@}. The arguments as an array (without the name of the macro) is available as ${#}. The more traditional * could not be used because it clashes with wildcard keys, it means ALL values.
+For example:
+foo: Hello ${1} -> ${foo;Peter} -> "Hello Peter"
+Keys can be wildcarded. For example, if you want to set -plugin from different places, then you can set the plugin.xxx properties in different places and combine them with -plugins= ${plugins.*}.
The ./ sequence is automatically expanded to the current filename when found in a macro source file. This generally what you want but unfortunately not always. The ./ prefix is only replaced when:
So, do you really require the ./ without expansion? If so, then there are the following solutions. The first one is to use another macro to break the sequence:
.=.
+Some-Header: ${.}/conf/admin.xml
+Alternatively there are a couple of macros that return the given value when called appropriately, and thereby break the sequence:
+Some-Header-1: ${def;.;.}/conf/jetty/admin.xml
+Some-Header-2: ${uniq;.}/conf/jetty/admin.xml
+Some-Header-3: ${unescape;.}/conf/jetty/admin.xml
+In many places a header is used to indicate false or true. In those cases we use some heuristics. The header/macro or whatever is false when:
+If the value starts with ! and text follows, the ! is removed and the remaining text is interpreted as a boolean and then negated.
In other cases, the value is considered true.
@TODO
+ + diff --git a/org.bndtools.help/docs/chapters/855-macros-ref.html b/org.bndtools.help/docs/chapters/855-macros-ref.html new file mode 100644 index 0000000000..c3437f8184 --- /dev/null +++ b/org.bndtools.help/docs/chapters/855-macros-ref.html @@ -0,0 +1,37 @@ + + + + +Learn the basics of bnd macros in the Macro reference and our bnd cheatsheet. Also check out the corresponding unit tests for the macros on this page.
+ + + diff --git a/org.bndtools.help/docs/chapters/870-plugins.html b/org.bndtools.help/docs/chapters/870-plugins.html new file mode 100644 index 0000000000..c6d5dddcce --- /dev/null +++ b/org.bndtools.help/docs/chapters/870-plugins.html @@ -0,0 +1,32 @@ + + + + +Plugins are objects that can extend the functionality of bnd. They are called from inside bnd when a certain action should take place. For example, bnd uses a repository and plugins provide the actual repository implementations. Or for example, the SpringComponent analyzes the Spring files and adds references found in that XML to the imports.
+A plugin is defined as:
+PLUGIN ::= FQN ( ';' \<directive\|attribute\> )*
+The following directive is defined for all plugin:
+||path: ||A path to the jar file that contains the plugin. The directory/jar at that location is placed on your classpath for that plugin.||
Repository plugins are usually referenced in cnf/build.bnd and implement the Tagged interface.
The tags property of repositories' configuration allows to add a comma separated list of tags to a repository. These tags will be used for filtering a list of repositories.
+For example the -runrepos instruction in .bndrun considers only those repositories for resolution which have either the resolve tag or no tags property defined. This allows including and excluding repositories based on their tags.
External Plugins are external code to bnd code but that can be executed from within bnd. The JARs for this code are coming from the Workspace repository. The External Plugin Namespace defines the namespace (bnd.external.plugin) and the following attributes:
| Attribute | Description |
+|-------------------------------+-------------------------------|
+| bnd.external.plugin | Defines the name of the plugin, should follow simple token syntax|
+| objectClass | The interface type the implementation should implement|
+| implementation | The implementation fully qualified class name|
+| subtype | Optional subtype when the objectClass has a type parameter |
+| version | Optional version range to limit the candidates |
There is an annotation, aQute.bnd.service.externalplugin.ExternalPlugin, that can be applied on a type.
For example:
+aQute.bnd.service.externalplugin.ExternalPlugin(
+ name = "calling",
+ objectClass = Callable.class,
+ subtype = String.class)
+public class CallImpl implements Callable<String> {
+ public String call() throws Exception {
+ return "hello";
+ }
+}
+In Bndtools, you can declare any class as an ExternalPlugin. The automatic build features will automatically build the JAR of the plugin and this will immediately become available in the rest of the build. If you use external plugins from the local workspace, make sure to declare a -dependson to the external plugin project in any project that uses it, this dependency is not automatically detected.
A JAR can contain any number of external plugins. It must ensure that it does not have any dependencies outside the bndlib it was compiled against.
+ + diff --git a/org.bndtools.help/docs/chapters/880-settings.html b/org.bndtools.help/docs/chapters/880-settings.html new file mode 100644 index 0000000000..f8262e81d4 --- /dev/null +++ b/org.bndtools.help/docs/chapters/880-settings.html @@ -0,0 +1,35 @@ + + + + +For a build model it is crucial that it has no dependencies on the machine it is running on. One of the most frustrating developer experiences is the phrase: 'Yes, but it runs on my machine!'. For this reason bnd goes out of it is way to have no dependencies outside its workspace. That is, until we hit security. It is in the nature of security to have secrets and by definitions these secrets cannot be in the workspace, or, well, they will not be that secret for long. Though the best solution is to keep the secret in your head, there are times when this is impossible or plain cumbersome.
+For this reason, bnd maintains a settings file in ~/.bnd/settings.json. This file contains ordinary settings ...
+TBD
+The settings automatically generate a private and public key when they are initialized. However, in certain cases it is necessary to explicitly authorize a system. For example, Travis always starts a build from a completely freshly initialized VM. In such a case it is necessary to authorize the machine explicitly. Though it is possible to just copy an existing settings file to this machine, it is more elegant to use the bnd commands. Do the following steps to authorize a machine.
+On a trusted host, get the current private and public key and the email:
+$ bnd identity
+--publicKey 08E...CF --privateKey C7FE...D3 --email bnd@example.org
+The output is a bit longer than shown because these keys are darned long, you know, security is hard. You should copy the output and then on the target system run:
+$ bnd identity <copied output>
+This will use the same authorization as the original machine. If you want to create a more unique authorization, then it is possible to create a new private/public key pair.
+ $ bnd settings -l temp.json -g
+ $ bnd identity -l temp.json
+ --publicKey 08E...CF --privateKey C7FE...D3 --email bnd@example.org
+ ...
+ $ bnd identity <copied output>
+First to explain the message. In a Bndtools project, the Java version is configured in two places. For the offline bnd build with Gradle/Ant, we set it directly from the javac.source and javac.target properties that you find in cnf/build.bnd. But in the internal Eclipse build, the configuration of the Eclipse project contains the JRE settings. Unfortunately we cannot force Eclipse to use the same settings as the offline build, but we can detect when they are inconsistent, and so we generate the warning you have seen.
To fix the problem, first decide what version of Java you want to use. Then you need to set that in both the bnd configuration and in the Eclipse workspace.
+For bnd, you can set javac.source and javac.target for the whole workspace in cnf/build.bnd, and you can override it if you wish in each project's bnd.bnd file. You can find these entries in cnf/build.bnd from the standard templates, it will be commented out so just uncomment it.
For Eclipse you can set a workspace-wide preference by opening Window -> Preferences and navigating to the Java -> Compiler preference panel. This can also be overridden on a per-project basis by right-clicking the project, selecting Properties and navigating to the Java Compiler property panel. To be really safe, it's best to set the Eclipse compiler properties explicitly per-project. This is because these properties are persisted by Eclipse into the .settings folder inside the project, which can be checked into source control, whereas the workspace-wide settings go into the workspace .metadata directory which is not usually checked in.
You can use the bnd discourse site mail list or mail me.
+Sometimes bnd reports imports that seem plain wrong. Believe me, they are almost always right. bnd does a thorough analysis of the byte codes in your class files and when it imports something it is almost sure to be a reference in your code. How can you find the culprit? Bndtools has tooling to drill down into your code. bnd also can print out the JAR and look at the [USEDBY] section to find the package(s) that cause the import.
If there is no package using the imported package, then look at the following places for imports:
+In the likely case the import is real but unwanted, look at Unwanted Imports.
+This usually indicates that:
+If you have an unwanted import than you can remove it from the Import-Package manifest header with the '!' operator:
+Import-Package: !com.unwanted.reference.*, *
+A usually better way is to make the imports optional:
+Import-Package: com.unwanted.reference.*;resolution:=optional, *
+Note the end at the Import-Package statement, that wildcard '*' is crucial for remaining imports, see No Imports Show Up.
+The imports that show up in your Import-Package manifest header are controlled by the bnd file's Import-Package instruction. In bnd, the list is a set of patterns that are sequentially applied to your imports as calculated by bnd from the classes, resources, and headers in the JAR.
+The default Import-Package bnd instruction is:
+Import-Package: *
+This imports all referred packages. The most common reasons imports do not appear in the manifest is that the default is overridden and the overrider forgot to add the wildcard '*' at the end. For example, the following is wrong:
+Import-Package: com.example; version=1.2
+This will create an import for com.example but it ignores all other imports.
+Another reason is the exclude operator ('!') that will remove imports. If this is too wild, then no imports are left to insert in the manifest. So the following leads to no imports for any package starting with com.example.
+Import-Package: !com.example.*, *
+Last but not least, look at the [USEDBY] section of the JAR print out, make sure there are actually references.
With -pedantic:true bnd creates a warning like
++Imports that lack version ranges due to not being found in any bundle on the -buildpath:
+com.example.foo.bar...
These could stem from transitive dependencies of the jars on your buildpath.
+There are multiple reasons for that, for example they could stem from transitive dependencies:
+- of non-OSGi jars which are included via -includeresource
+- of OSGi bundles on your classpath, if the import version ranges for those bundles are not specified in the Bundle. E.g. if the bundle imports the dependency via Require-Bundle it won't have the import range, or if it uses Import-Package with no import range.
Many people are surprised that bnd does not automatically calculate the Bundle-Activator. Basically, there are the following issues:
+That said, it is possible to automate the Bundle-Activator:
+Bundle-Activator: \
+ ${classes;IMPLEMENTS;org.osgi.framework.BundleActivator}
+However, if there are multiple Bundle-Activators you will get an error.
+@Reference automatically sets the bind method but how is the unbind method set? Simple, you use a method with a similar name:
| bind | +unbind | +
|---|---|
| setX | +unsetX | +
| addX | +removeX | +
For example:
+@Reference
+protected void setFoo(LogService l) { ... }
+protected void unsetFoo(LogService l) { ... }
+If you want to override this, use
+@Reference(unbind="IRefuseToCallMyMethodUnFoo");
+protected void foo(LogService l) {}
+protected void IRefuseToCallMyMethodUnFoo(LogService l) {}
+Unfortunately Java has no method references so it is not type safe.A non existent @UnReference annotation is not very useful because that still requires linking it up symbolically to the associated @Reference.
I'm just a little bit confused about the bnd approach with the file naming "packageinfo". The JLS already defines "package-info.java" for package relevant infos. Wouldn't it be simplier (less complex) to have only one file for package definitions? So, the bnd tool could manage the package version in "package-info.java", too.
+You can use either file. The reason there are two options is that one constituency thinks annotations are the only solution and the other is running pre Java 5 ... Basically if I would have to choose it would be packageinfo because that works anywhere but I expect that you probably would not like that :-)
+Annotations are not inherited from the component's super classes by default. The problem is that super classes from imported packages may be different at runtime than they were at build time. So it is always best to declare your annotations on the actual component class. Alternatively you can use the instruction -dsannotations-options: inherit. Then bnd will use DS annotations found in the class hierarchy of the component class. This will not work for the @Component annotation itself; it will not be inherited from super classes causing a subclass to suddenly be a component.
Starting with bnd 7.3.0, bnd enforces the DS specification requirement that component classes must have either a public no-argument constructor or a public constructor annotated with @Activate for constructor injection.
If you see this error:
+The DS component class {className} must be publicly accessible and have either a public no-arg constructor or a public constructor annotated with @Activate. Non-public classes, including public inner classes enclosed in non-public classes, are not supported.
+You need to either add a public no-arg constructor, use @Activate on your constructor, ensure your class is public, or make inner classes static.
See Component Class Requirements for detailed explanations and code examples.
+If no explicit export version is specified in the bnd file then bnd will look in the following places.
+The use of the bundle version is a rather bad idea, packages should be versioned independently. It is possible +to not use the bundle version for a package with:
+-nodefaultversion true
+No. It creates unnecessary complexity, it is slower, and it is not necessary. It will also not be compatible with techniques like PojoSR, something that the OSGi is looking into standardizing.
+Just use Private-Package and Export-Package, know what goes into your JAR. If you really need to wrap one or more JARs, use the Include-Resource instruction, it has an option to unroll a JAR resource (see @ option. This will copy all its contents in the target JAR. The -exportcontents can then be used to export selected packages. Even better is of course to know
+The Best Practices section provides some good tips about how you setup workspaces that share some information.
+If an entry is wrong, send a mail with the JAR that has the problem. Preferably as small as possible.
+ + diff --git a/org.bndtools.help/docs/commands/action.html b/org.bndtools.help/docs/commands/action.html new file mode 100644 index 0000000000..56719444fe --- /dev/null +++ b/org.bndtools.help/docs/commands/action.html @@ -0,0 +1,21 @@ + + + + + action [options] ...
+[ -f --filter <glob> ] [ -p --project <string> ] Identify another project[ -t --tooltip ] add <what> ...
+fragment - Add template fragment(s) to the current workspace. plugin - Add a plugin project - Create a project in the current workspace workspace - Create a bnd workspace in the current folder. The workspace can also be inialized with a set of template fragments. Add template fragment(s) to the current workspace. Leave the name empty to see a list of available templates at the index.\n\nExample:\n bnd add fragment osgi gradle
+ fragment <[name]...>
+Add a plugin
+ plugin [options] <[name]> ...
+[ -a --alias <string> ] [ -f --force ] Create a project in the current workspace
+ project <name> ...
+Create a bnd workspace in the current folder. The workspace can also be inialized with a set of template fragments.
+Example: + bnd add workspace -f osgi -f gradle 'myworkspace'
+See https://bnd.bndtools.org/chapters/620-template-fragments.html for more information.
+ workspace [options] <name> ...
+[ -f --fragment <string>* ] Specify template fragment(s) by name to install together with the created workspace. Fragments are identified by the 'name' attribute in the index. Specify multiple fragments by repeating the -f option. To see a list of available templates use 'bnd add fragment' without arguments. baseline [options] <[newer jar]> <[older jar]>
+[ -a --all ] Show all, also unchanged[ -d --diff ] Show any differences[ -f --fixup <string> ] Output file with fixup info[ -p --packages <string> ] Packages to baseline (comma delimited)[ -q --quiet ] Be quiet, only report errors[ -v --verbose ] On changed, list API changesbnd baseline --diff newer.jar older.jar
bash ...
+The bash command in bnd can generate an autocompletion script for the Bash shell, making it easier to use bnd commands interactively.
When you run the bash autocompletion generator, bnd will create a temporary file containing the autocompletion script, populate it with the list of available commands, and output the result. This script can then be sourced in your shell to enable tab-completion for bnd commands.
+Example:
+To generate and use the autocompletion script for bash:
+bnd bash > bnd-completion.bashsource bnd-completion.bashThis will enable tab-completion for all available bnd commands in your current shell session.
+TODO Needs review - AI Generated content
+ bnd [options] ...
+[ -b --base <string> ] Use as base directory[ -d --debug ] Show log debug output[ -e --exceptions ] Print out stack traces when there is an unexpected exception[ -f --failok ] Turns errors into warnings so command always succeeds[ -i --ignore <string;> ] Error/Warning ignore patterns[ -o --output <file> ] Redirect output[ -p --pedantic ] Report errors pedantically[ -s --secret <[c> ] Provide a settings password[ -t --trace ] Trace progress[ -w --workspace ] Use the workspace related to the default directory as parent for bnd. If the current directory is not related to a workspace this option is silently ignored bootstrap ...
+The bootstrap command in bnd is used to start a local OSGi framework using the workspace's configuration. This command looks for the bnd.bnd file in the workspace's cnf directory and uses it to launch the framework locally.
If the bnd.bnd file is not found, the command will report an error and exit. Otherwise, it will initialize the framework and run it in the local environment, making it easy to test or develop OSGi applications directly from your workspace setup.
Example:
+To start a local framework using the workspace configuration:
+bnd bootstrap
+This will launch the OSGi framework as defined by your workspace's bnd.bnd file.
TODO Needs review - AI Generated content
+ bsn2url [options] ...
+[ -p --project <string> ] Identify another projectThe bsn2url command in bnd is used to generate a list of URLs for bundles based on their bundle symbolic names (bsns) and versions. This is useful for creating lists of bundle locations for deployment, analysis, or documentation purposes.
The command reads a list of bsns (and optional version ranges) from input files, looks up the available versions in the configured repositories, and outputs the corresponding URLs for each matching bundle.
+Example:
+Suppose you have a file bundles.txt containing:
com.example.foo;version='[1.0,2.0)'
+com.example.bar
+You can generate a list of URLs for these bundles with:
+bnd bsn2url bundles.txt
+This will print the URLs for the matching bundles and versions, making it easy to retrieve or reference them in other tools or scripts.
+TODO Needs review - AI Generated content
+ build [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -f --force ] Force non-incremental[ -p --parallel ] Build in parallel (Experimental)[ -P --project <string> ] Identify another project[ -s --synctime <long> ] [ -t --test ] Build for test[ -v --verbose ] prints more processing information[ -w --watch ] Continuous incremental build[ -W --workspace <string> ] Use the following workspace buildtool [options] ...
+[ -f --force ] [ -w --workspace <string> ] Use the following workspace buildx [options] ...
+[ -c --classpath <string>* ] [ -e --eclipse ] [ -f --force ] [ -n --noeclipse ] [ -o --output <string> ] [ -p --pom ] [ -s --sourcepath <string>* ] [ -S --sources ] bnd buildx -classpath bin -noeclipse -output test.jar xyz.bnd
bump [options] <<major|minor|micro>>
+[ -p --project <string> ] Path to another project than the current project changes [options]
+[ -a --all ] Print all releases classpath [options]
+[ -l --list ] As list[ -p --project <string> ] Identify another project[ -s --separator <string> ] Define the separator, default is platform dependent path separator[ -t --testpath ] Include -testpath dependencies[ -x --xoutput ] Exclude output folders classtoresource ...
+ clean [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace collect [options] ...
+[ -c --classes <string> ] A file with a list of class names to extract. Can be -- when the names are piped into this command com ...
+clear - Clear the cached file that is associated with the givenURI info - Show the information used by the Http Client to get aremote file settings - Show the bnd -connection-settings Clear the cached file that is associated with the givenURI
+ clear ...
+Show the information used by the Http Client to get aremote file
+ info [options] <url...>
+[ -c --cached ] Use the cache option. If this option is not used then thefile will be refreshed[ -o --output <string> ] Save the remote content in the given file. If you use '.'then the file shown to the outputShow the bnd -connection-settings
+ settings ...
+ compile [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -t --test ] Compile for test[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace convert [options] <from> <to>
+[ -m --m2p ] Convert a manifest to a properties files[ -x --xml ] Save as xml copy [options] <src...> <dest>
+[ -o --optional ] Remove OSGI-OPT[ -s --specific ] Remove OSGi metadata from the manifest[ -S --strip ] Remove all metadata manifest create [options] ...
+[ -b --bsn <string> ] Bundle Symbolic Name for wrap[ -c --cdir <string> ] directory[ -f --file <string> ] Jar file (f option)[ -F --force ] Force write event if there are errors[ -m --manifest <string> ] Use manifest (m option)[ -n --nocompression ] No compression (0 option)[ -p --properties <string> ] Properties for wrapping[ -s --skipmanifest ] No manifest.[ -v --verbose ] Verbose (v option)[ -V --version <version> ] Bundle Version for wrap[ -w --wrap ] Wrap debug [options] ...
+[ -f --flattened ] Show the flattened properties[ -p --project <string> ] Path to a project, default is current directory defaults ...
+ deliverables [options]
+[ -l --limit ] Only provide deliverables of this project[ -p --project <string> ] Path to project, default current directorybiz.aQute.bnd (master)$ bnd deliverables
+found password
+aQute.libg 2.9.0 /Ws/bnd/aQute.libg/generated/aQute.libg.jar
+biz.aQute.bnd 2.4.0 /Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar
+biz.aQute.bnd.annotation 2.4.0 /Ws/bnd/biz.aQute.bnd.annotation/generated/biz.aQute.bnd.annotation.jar
+biz.aQute.bnd.bootstrap.console 0.0.0 /Ws/bnd/biz.aQute.bnd.bootstrap/generated/biz.aQute.bnd.bootstrap.console.jar
+biz.aQute.bnd.test 2.4.0 /Ws/bnd/biz.aQute.bnd.test/generated/biz.aQute.bnd.test.jar
+biz.aQute.bnd.testextension 2.4.0 /Ws/bnd/biz.aQute.bnd.testextension/generated/biz.aQute.bnd.testextension.jar
+biz.aQute.bndlib 2.4.0 /Ws/bnd/biz.aQute.bndlib/generated/biz.aQute.bndlib.jar
+biz.aQute.bndlib.tests 2.4.0 /Ws/bnd/biz.aQute.bndlib.tests/generated/biz.aQute.bndlib.tests.jar
+biz.aQute.junit 1.3.0 /Ws/bnd/biz.aQute.junit/generated/biz.aQute.junit.jar
+biz.aQute.launcher 1.4.0 /Ws/bnd/biz.aQute.launcher/generated/biz.aQute.launcher.jar
+biz.aQute.repository 2.2.0 /Ws/bnd/biz.aQute.repository/generated/biz.aQute.repository.jar
+biz.aQute.resolve 0.2.0 /Ws/bnd/biz.aQute.resolve/generated/biz.aQute.resolve.jar
+cnf 0.0.0 /Ws/bnd/cnf/generated/cnf.jar
+demo 1.1.0 /Ws/bnd/demo/generated/demo.jar
+dist 0.0.0 /Ws/bnd/dist/generated/dist.jar
+osgi.r5 1.0.1 /Ws/bnd/osgi.r5/generated/osgi.r5.jar
+biz.aQute.bnd (master)$ bnd deliverables -l
+found password
+biz.aQute.bnd 2.4.0 /Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar
+biz.aQute.bnd (master)$
+ dependencies [options] <instruction...>
+[ -c --count ] Show the number of projects using that dependency[ -p --project <string> ] Identify another project dev [options] <[bndrun...]>
+[ -e --exclude <string;> ] Exclude files by pattern[ -f --force ] Force non-incremental[ -p --parallel ] Do the initial full build in parallel (Experimental)[ -P --project <string> ] Path to another project than the current project. Only valid if no bndrun is specified[ -s --synctime <long> ] [ -t --test ] Build for test[ -v --verbose ] prints more processing information[ -V --verify ] Verify all the dependencies before launching (runpath, runbundles)[ -w --workspace <string> ] Use the following workspace diff [options] <[newer file]> <[older file]>
+[ -a --api ] Print the API[ -f --full ] Show full tree[ -i --ignore <string>* ] Ignore headers[ -m --manifest ] Print the Manifest[ -o --output <file> ] Where to send the output[ -p --pack <string>* ] Limit to these packages (can have wildcards)[ -r --resources ] Print the Resources[ -x --xml ] Print difference as valid XMLbiz.aQute.bnd (master)$ bnd diff generated/
+generated/.index generated/biz.aQute.bnd.jar generated/buildfiles
+biz.aQute.bnd (master)$ bnd diff generated/biz.aQute.bnd.jar /Ws/archive/aQute.archive/aQute.bnd/archive/tmp/biz.aQute.bnd.jar
+ADDED PACKAGE aQute.bnd.build
+ADDED PACKAGE aQute.bnd.header
+ADDED PACKAGE aQute.bnd.maven.support
+ADDED PACKAGE aQute.bnd.osgi
+ADDED PACKAGE aQute.bnd.osgi.resource
+ADDED PACKAGE aQute.bnd.service
+ADDED PACKAGE aQute.bnd.service.action
+ADDED PACKAGE aQute.bnd.service.classparser
+ADDED PACKAGE aQute.bnd.service.diff
+ADDED PACKAGE aQute.bnd.service.extension
+ADDED PACKAGE aQute.bnd.service.progress
+ADDED PACKAGE aQute.bnd.service.repository
+ADDED PACKAGE aQute.bnd.service.resolve.hook
+ADDED PACKAGE aQute.bnd.service.url
+ADDED PACKAGE aQute.bnd.util.dto
+ADDED PACKAGE aQute.bnd.version
+REMOVED PACKAGE aQute.lib.osgi
+ADDED PACKAGE aQute.service.reporter
+ADDED PACKAGE org.osgi.resource
+ADDED PACKAGE org.osgi.service.bindex
+ADDED PACKAGE org.osgi.service.repository
+ADDED CLASS_VERSION J2SE6
+CHANGED MANIFEST <manifest>
+REMOVED HEADER Bundle-ActivationPolicy:lazy
+REMOVED HEADER Bundle-Activator:aQute.bnd.plugin.Activator
+REMOVED HEADER Bundle-Copyright:2006, 2007 (c) aQute, All rights reserved
+ADDED HEADER Bundle-Copyright:Copyright (c) aQute (2000, 2014). All Rights Reserved.
+REMOVED HEADER Bundle-Description:A utility and plugin to wrap, build, or print bundles
+ADDED HEADER Bundle-Description:This command line utility is the Swiss army knife of OSGi. It provides you with a breadth
+CHANGED HEADER Bundle-License
+ REMOVED CLAUSE ASL20
+ ADDED CLAUSE http://www.opensource.org/licenses/apache2.0.php
+REMOVED HEADER Bundle-Name:aQute Bundle Tool
+ADDED HEADER Bundle-Name:biz.aQute.bnd
+ADDED HEADER Bundle-SCM:git://github.com/bndtools/bnd.git
+CHANGED HEADER Bundle-SymbolicName
+ CHANGED CLAUSE biz.aQute.bnd
+ REMOVED PARAMETER singleton::true
+REMOVED HEADER Bundle-Version:0.0.279
+ADDED HEADER Bundle-Version:2.4.0
+REMOVED HEADER Conditional-Package:aQute.libg.*, aQute.lib.*
+ADDED HEADER Conditional-Package:aQute.libg.*,aQute.lib.*,aQute.configurable
+CHANGED HEADER Export-Package
+ ADDED CLAUSE aQute.bnd.build
+ ADDED CLAUSE aQute.bnd.header
+ ADDED CLAUSE aQute.bnd.maven.support
+ ADDED CLAUSE aQute.bnd.osgi
+ ADDED CLAUSE aQute.bnd.osgi.resource
+ ADDED CLAUSE aQute.bnd.service
+ ADDED CLAUSE aQute.bnd.service.action
+ ADDED CLAUSE aQute.bnd.service.classparser
+ ADDED CLAUSE aQute.bnd.service.diff
+ ADDED CLAUSE aQute.bnd.service.extension
+ ADDED CLAUSE aQute.bnd.service.progress
+ ADDED CLAUSE aQute.bnd.service.repository
+ ADDED CLAUSE aQute.bnd.service.resolve.hook
+ ADDED CLAUSE aQute.bnd.service.url
+ ADDED CLAUSE aQute.bnd.util.dto
+ ADDED CLAUSE aQute.bnd.version
+ REMOVED CLAUSE aQute.lib.osgi
+ ADDED CLAUSE aQute.service.reporter
+ ADDED CLAUSE org.osgi.resource
+ ADDED CLAUSE org.osgi.service.bindex
+ ADDED CLAUSE org.osgi.service.repository
+ADDED HEADER Git-Descriptor:2.4.0.M1-66-gc1ad07d-dirty
+ADDED HEADER Git-SHA:c1ad07dfeb4704ce590bd93c1405d7bfe8bef131
+CHANGED HEADER Import-Package
+ ADDED CLAUSE aQute.bnd.service
+ ADDED CLAUSE aQute.bnd.service.action
+ ADDED CLAUSE aQute.bnd.service.diff
+ ADDED CLAUSE aQute.bnd.service.progress
+ ADDED CLAUSE aQute.bnd.service.repository
+ ADDED CLAUSE aQute.bnd.service.url
+ ADDED CLAUSE aQute.bnd.version
+ REMOVED CLAUSE aQute.lib.filter
+ REMOVED CLAUSE aQute.lib.osgi
+ REMOVED CLAUSE aQute.libg.generics
+ REMOVED CLAUSE aQute.libg.header
+ REMOVED CLAUSE aQute.libg.qtokens
+ REMOVED CLAUSE aQute.libg.reporter
+ REMOVED CLAUSE aQute.libg.version
+ ADDED CLAUSE aQute.service.reporter
+ ADDED CLAUSE javax.crypto
+ ADDED CLAUSE javax.crypto.spec
+ ADDED CLAUSE javax.naming
+ ADDED CLAUSE javax.net.ssl
+ ADDED CLAUSE javax.script
+ ADDED CLAUSE javax.xml.namespace
+ ADDED CLAUSE javax.xml.transform
+ ADDED CLAUSE javax.xml.transform.dom
+ ADDED CLAUSE javax.xml.transform.stream
+ ADDED CLAUSE javax.xml.xpath
+ ADDED CLAUSE junit.framework
+ CHANGED CLAUSE org.apache.tools.ant
+ ADDED PARAMETER resolution::optional
+ ADDED CLAUSE org.apache.tools.ant.taskdefs
+ CHANGED CLAUSE org.apache.tools.ant.types
+ ADDED PARAMETER resolution::optional
+ REMOVED CLAUSE org.eclipse.core.resources
+ REMOVED CLAUSE org.eclipse.core.runtime
+ REMOVED CLAUSE org.eclipse.debug.core
+ REMOVED CLAUSE org.eclipse.debug.ui
+ REMOVED CLAUSE org.eclipse.debug.ui.sourcelookup
+ REMOVED CLAUSE org.eclipse.jdt.core
+ REMOVED CLAUSE org.eclipse.jdt.debug.ui.launchConfigurations
+ REMOVED CLAUSE org.eclipse.jdt.internal.junit.launcher
+ REMOVED CLAUSE org.eclipse.jdt.junit.launcher
+ REMOVED CLAUSE org.eclipse.jdt.launching
+ REMOVED CLAUSE org.eclipse.jdt.ui.wizards
+ REMOVED CLAUSE org.eclipse.jface.action
+ REMOVED CLAUSE org.eclipse.jface.dialogs
+ REMOVED CLAUSE org.eclipse.jface.resource
+ REMOVED CLAUSE org.eclipse.jface.text
+ REMOVED CLAUSE org.eclipse.jface.text.source
+ REMOVED CLAUSE org.eclipse.jface.viewers
+ REMOVED CLAUSE org.eclipse.jface.wizard
+ REMOVED CLAUSE org.eclipse.swt.events
+ REMOVED CLAUSE org.eclipse.swt.graphics
+ REMOVED CLAUSE org.eclipse.swt.layout
+ REMOVED CLAUSE org.eclipse.swt.widgets
+ REMOVED CLAUSE org.eclipse.ui
+ REMOVED CLAUSE org.eclipse.ui.part
+ REMOVED CLAUSE org.eclipse.ui.plugin
+ REMOVED CLAUSE org.eclipse.ui.texteditor
+ CHANGED CLAUSE org.osgi.framework
+ ADDED PARAMETER version:[1.6,2)
+ ADDED CLAUSE org.osgi.resource
+ ADDED CLAUSE org.osgi.service.log
+ REMOVED CLAUSE org.osgi.service.packageadmin
+ ADDED CLAUSE org.xml.sax.helpers
+REMOVED HEADER Include-Resource:plugin.xml, LICENSE, maven-dependencies.txt
+ADDED HEADER Private-Package:aQute.bnd.annotation;version="1.43.2",aQute.bnd.annotation.component;version="1.43.1",aQute.bnd.annotation.headers;version="1.0",aQute.bnd.annotation.licenses;version="1.0",aQute.bnd.annotation.metatype;version="1.44.1",aQute.bnd.ant,aQute.bnd.build.model;version="2.6.0",aQute.bnd.build.model.clauses;version=2,aQute.bnd.build.model.conversions,aQute.bnd.compatibility,aQute.bnd.component,aQute.bnd.component.error;version="1.0.0",aQute.bnd.differ;version="1.1.0",aQute.bnd.enroute.commands,aQute.bnd.filerepo;version="1.0",aQute.bnd.gradle,aQute.bnd.help;version="1.1",aQute.bnd.indexer,aQute.bnd.indexer.analyzers,aQute.bnd.main;version="0.9",aQute.bnd.make,aQute.bnd.make.calltree,aQute.bnd.make.component,aQute.bnd.make.coverage,aQute.bnd.make.metatype,aQute.bnd.maven,aQute.bnd.obr,aQute.bnd.osgi.eclipse,aQute.bnd.properties;version="2.0",aQute.bnd.resource.repository,aQute.bnd.signing,aQute.bnd.testing;version="1.0",aQute.bnd.url;version="1.0",aQute.configurable;version="1.0.0",aQute.lib.deployer,embedded-repo.jar,org.osgi.service.component.annotations;version="1.3",org.osgi.service.coordinator;version="1.0",templates,aQute.lib.base64;version="1.2.0",aQute.lib.collections;version="1.2.0",aQute.lib.converter;version="2.0.1",aQute.lib.filter;version="1.1.0",aQute.lib.getopt;version="1.0.0",aQute.lib.hex;version="1.1.0",aQute.lib.io;version="1.4.0",aQute.lib.json;version="3.0.0",aQute.lib.justif;version="1.1.0",aQute.lib.persistentmap;version="1.1.0",aQute.lib.settings;version="1.2.0",aQute.lib.strings;version="1.1.0",aQute.lib.tag;version="1.1",aQute.libg.classdump;version="1.0",aQute.libg.command;version="3.0.0",aQute.libg.cryptography;version="1.1.0",aQute.libg.filelock;version="1.0.0",aQute.libg.filters;version="1.0",aQute.libg.forker;version="1.0",aQute.libg.generics;version="1.0",aQute.libg.glob;version="1.1.1",aQute.libg.map;version="1.2.0",aQute.libg.qtokens;version="1.0",aQute.libg.reporter;version="1.5",aQute.libg.sed;version="1.1.0",aQute.libg.tuple;version="1.0",aQute.lib.markdown
+REMOVED HEADER Private-Package:aQute.lib.deployer,aQute.bnd.test,aQute.bnd.junit,aQute.bnd.launch,aQute.lib.jardiff,aQute.bnd.build,aQute.bnd.plugin.popup.actions,aQute.bnd.annotation,aQute.bnd.service,aQute.lib.osgi.eclipse,aQute.bnd.ant,aQute.bnd.main;version="0.9",aQute.bnd.plugin,aQute.bnd.make,aQute.bnd.plugin.builder,aQute.bnd.jareditor,aQute.bnd.classpath
+ADDED HEADER Require-Capability:osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))"
+MAJOR RESOURCES <resources>
+MAJOR RESOURCE LICENSE
+ ADDED SHA 2B8B815229AA8A61E483FB4BA0588B8B6C491890
+ REMOVED SHA 47B573E3824CD5E02A1A3AE99E2735B49E0256E4
+REMOVED RESOURCE OSGI-OPT/bnd.bnd
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/ConsumerType.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/annotation/Export.java
+ ADDED SHA 57B5F624DE03F979D4876F63422BA92203EF1910
+ REMOVED SHA 6A6378BCE886CA435A3F166F70F0E0AB12828B13
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/Import.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/Pair.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/ProviderType.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/Version.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/Activate.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/Component.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/ConfigurationPolicy.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/Deactivate.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/Modified.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/Reference.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/component/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/About.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleCategory.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleContributors.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleCopyright.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleDevelopers.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleDocURL.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/BundleLicense.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/Category.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/ProvideCapability.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/RequireCapability.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/Resolution.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/headers/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/ASL_2_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/BSD_2_Clause.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/BSD_3_Clause.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/CDDL_1_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/CPL_1_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/EPL_1_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/GPL_2_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/GPL_3_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/LGPL_2_1.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/MIT_1_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/MPL_2_0.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/licenses/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/metatype/Configurable.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/metatype/Meta.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/metatype/packageinfo
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/package-info.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/annotation/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/AntMessages.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/BaseTask.java
+ REMOVED SHA 45CF7C2907DD82AD23A979E880C20007206AA0E6
+ ADDED SHA C27D9C96DFCDB750A1FDBA926EE99A1ED5B154B2
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/BndTask.java
+ ADDED SHA 881EC2597543A91A9F1A883817C55D2B80F12CB7
+ REMOVED SHA ABCC53977C4924B29EDF334DA1B3FE5010EFD3B6
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/ConsoleProgress.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/DeployTask.java
+ ADDED SHA 33ED2BB9356BCFBD7A0D0AF8EB1BB10B6D7E7C7D
+ REMOVED SHA CF24BA22F1A33E61F950E34317F0D82082B08921
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/EclipseTask.java
+ REMOVED SHA 62E6497D3868BD6E48C86689E6CE26157A33AC6D
+ ADDED SHA B6D59DF94689FEEE1166D9EAC0938CCF92812115
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/ExpandPropertiesTask.java
+ REMOVED SHA 08B914BB997480B60C3F48E8A5CB4BD1E7E84EF9
+ ADDED SHA B429909161768A44F2BE870C43E3F3DA84E6D827
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/PackageTask.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/PrepareTask.java
+ REMOVED SHA 60468618217981C8ED498CE4D66668C7D3A0459B
+ ADDED SHA 949B04B29AEBC412A3485BF9AFAF446ED292DBF8
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/ProjectBuildOrderTask.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/ProjectTask.java
+ ADDED SHA 9540543903DD936FFAD6C972D40C772C3B6CD3B0
+ REMOVED SHA 9D9BF171CB0AF435A92FA6F7F7BA6D6ADDAE8A22
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/ReleaseTask.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/RunconfigToDistributionTask.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/ant/TestTask.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/ant/WrapTask.java
+ ADDED SHA 164E838D471D1773C0010560A3B419EE5D666418
+ REMOVED SHA 62318CD6C8F4D42EFDA0C39D22F418185091A712
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/build/CircularDependencyException.java
+ ADDED SHA 2DBD8F2F5382CBF94E4F495B085C44770FD52796
+ REMOVED SHA 903622D5709FBB2D44120AC242F8A6BAB2ED9604
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/build/Container.java
+ REMOVED SHA 914441F7D5DBB2F86BFD83F4FAC9EB047E464ABD
+ ADDED SHA A897B7CAFD1004D6FA0F06625F25CAB5D19CBFA6
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/DownloadBlocker.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ErrorDetails.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/build/Framework.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/JUnitLauncher.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/build/Project.java
+ ADDED SHA C7A0428FE040DA06D28D865209F573B88D0ABCB5
+ REMOVED SHA D33BBA8757E45959B32ED2283B1E176DFF8A6BAE
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/build/ProjectBuilder.java
+ REMOVED SHA 4BC12A6E5EE452B093F8EDCFC7810D7E475C89B3
+ ADDED SHA 6069CBE1015591C5E02C3A63272EE499573A12A4
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ProjectLauncher.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ProjectMessages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ProjectTester.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ReflectAction.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ResolverMode.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/Run.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/ScriptAction.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/build/Workspace.java
+ REMOVED SHA 07AF5474B58E11D2806FBD6AAC0DBFB4B8F66EBC
+ ADDED SHA 6A9DEF93C7F8704B74059D56AFD4A7F279FB3D75
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/WorkspaceRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/BndEditModel.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/EE.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/ComponentSvcReference.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/ExportedPackage.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/HeaderClause.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/ImportPattern.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/ServiceComponent.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/VersionedClause.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/clauses/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/ClauseListConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/CollectionFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/Converter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/DefaultFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/EEConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/EEFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/EnumConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/EnumFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/HeaderClauseConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/MapFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/NoopConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/PropertiesConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/RequirementFormatter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/RequirementListConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/SimpleListConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/StringEntryConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/conversions/VersionedClauseConverter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/model/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/build/packageinfo
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/classpath/BndContainer.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/classpath/BndContainerInitializer.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/classpath/BndContainerPage.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/classpath/ModelListener.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/Access.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/GenericParameter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/GenericType.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/Kind.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/ParseSignatureBuilder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/RuntimeSignatureBuilder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/Scope.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/compatibility/Signatures.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/AnnotationReader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/ComponentDef.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/DSAnnotations.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/HeaderReader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/ReferenceDef.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/TagResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/error/DeclarativeServicesAnnotationError.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/component/error/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/Baseline.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/DiffImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/DiffPluginImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/Element.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/JavaElement.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/RepositoryElement.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/differ/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/enroute/commands/EnrouteCommand.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/enroute/commands/EnrouteOptions.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/filerepo/FileRepo.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/filerepo/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/header/Attrs.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/header/OSGiHeader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/header/Parameters.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/header/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/help/Syntax.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/help/Warnings.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/help/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/MimeType.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/Namespaces.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/RepoIndex.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/ResourceAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/BlueprintAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/BundleAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/EE.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/KnownBundleAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/OSGiFrameworkAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/SCRAnalyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/indexer/analyzers/Yield.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/jareditor/JarConfiguration.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/jareditor/JarDocumentProvider.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/jareditor/JarEditor.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/junit/OSGiArgumentsTab.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/junit/OSGiJUnitLaunchShortcut.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/junit/OSGiJUnitTabGroup.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/launch/LaunchDelegate.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/launch/LaunchTabGroup.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/launch/Shortcut.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/main/BaselineCommands.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/main/BndMessages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/main/DiffCommand.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/main/PatchCommand.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/main/RepoCommand.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/main/bnd.java
+ ADDED SHA 57EE3BDF196034FB708705A3F4656111277E9F97
+ REMOVED SHA 6CCDD322FC98BF0D7ACA4F26451A62F02D7A078A
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/main/packageinfo
+ REMOVED SHA 4CBF1AE09B541F925C0D6BCBB8684EF85A0B8373
+ ADDED SHA D6E9A3759405E18A266202C40D9AA4EBA6F83A87
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/make/Make.java
+ ADDED SHA 32419B285AD910CED3A1BE2CA2DE06103634D997
+ REMOVED SHA ABD503ABDB9F0A9D20FCDDB541B819EC0D70BA70
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/make/MakeBnd.java
+ ADDED SHA 7A16BD28EBD4A2334424FB9836644D4ABCBFFABC
+ REMOVED SHA C4FC212EB0584C41BA276F679A0F137802F387AF
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/make/MakeCopy.java
+ REMOVED SHA 06D298002296B2AEAFA4412515174712A2893ED2
+ ADDED SHA D4C879A84C0D44461049118CFD2B6DE835EC0295
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/calltree/CalltreeResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/component/ComponentAnnotationReader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/component/ServiceComponent.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/coverage/Coverage.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/coverage/CoverageResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/metatype/MetaTypeReader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/make/metatype/MetatypePlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/BsnToMavenPath.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenCommand.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenDependencyGraph.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenDeploy.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenDeployCmd.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenGroup.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/MavenRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/PomFromManifest.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/PomParser.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/PomResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/CachedPom.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/Maven.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/MavenEntry.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/MavenRemoteRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/Pom.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/ProjectPom.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/Repo.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/maven/support/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/obr/OBRFragment.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/About.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/AbstractResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Analyzer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/AnalyzerMessages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Annotation.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/AnnotationHeaders.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Builder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/BundleId.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/ClassDataCollector.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/ClassDataCollectors.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Classpath.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Clazz.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/CombinedResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/CommandResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Constants.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Contracts.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Descriptors.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Domain.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/EmbeddedResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/FileResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Instruction.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Instructions.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Jar.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/JarResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Macro.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/OSInformation.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/OpCodes.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Packages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/PreprocessResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Processor.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Resource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/URLResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/Verifier.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/WriteResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/ZipResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/eclipse/EclipseClasspath.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/CapReq.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/CapReqBuilder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/CapabilityImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/FilterParser.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/Filters.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/PersistentResource.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/RequirementImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/ResourceBuilder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/ResourceImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/osgi/resource/packageinfo
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/Activator.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/Central.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/builder/BndBuilder.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/builder/BndNature.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/builder/ToggleNatureAction.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/popup/actions/InstallBundle.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/popup/actions/MakeBundle.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/popup/actions/VerifyBundle.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/plugin/popup/actions/WrapBundle.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/BadLocationException.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/CopyOnWriteTextStore.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/Document.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/GapTextStore.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/IDocument.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/IRegion.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/ITextStore.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/Line.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/LineTracker.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/LineType.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/PropertiesLineReader.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/Region.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/properties/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/resource/repository/ResourceDescriptorImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/resource/repository/ResourceRepositoryImpl.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Actionable.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/service/AnalyzerPlugin.java
+ REMOVED SHA 01DCF34F1B2C6961BC001D6EB5D68BFFEEF15F70
+ ADDED SHA D25A7039580D56816F0E9ECE913C23644B43802A
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/BndListener.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/CommandPlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Compiler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/DependencyContributor.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Deploy.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/EclipseJUnitTester.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/IndexProvider.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/LauncherPlugin.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/service/MakePlugin.java
+ ADDED SHA 1DD964999442F1CEAED362243DA916643C348359
+ REMOVED SHA C6C083B804A2784D789182E00597B32CDFB89A78
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Plugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Refreshable.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Registry.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/RegistryDonePlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/RegistryPlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/RemoteRepositoryPlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/RepositoryListenerPlugin.java
+MAJOR RESOURCE OSGI-OPT/src/aQute/bnd/service/RepositoryPlugin.java
+ REMOVED SHA 3A6380D8D841D53668F18EF37363EFFA25A9FC73
+ ADDED SHA BC4B6801701A62CE8D4FD31090D31357D7595DC3
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/ResolutionPhase.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/ResourceHandle.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Scripter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/SignerPlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/Strategy.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/action/Action.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/action/NamedAction.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/action/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/classparser/ClassParser.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/classparser/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/Delta.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/Diff.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/Differ.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/Tree.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/Type.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/diff/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/extension/ExtensionActivator.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/extension/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/progress/ProgressPlugin.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/progress/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/InfoRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/MinimalRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/Phase.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/RepositoryDigest.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/ResourceRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/SearchableRepository.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/repository/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/resolve/hook/ResolverHook.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/resolve/hook/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/url/TaggedData.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/url/URLConnectionHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/url/URLConnector.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/service/url/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/signing/JartoolSigner.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/signing/Signer.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/bnd/test/ProjectLauncher.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/testing/DSTestWiring.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/testing/TestingLog.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/testing/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/BasicAuthentication.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/BndAuthentication.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/ConnectionSettings.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/DefaultURLConnectionHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/HttpsVerification.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/MultiURLConnectionHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/url/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/util/dto/DTO.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/util/dto/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/version/Version.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/version/VersionRange.java
+ADDED RESOURCE OSGI-OPT/src/aQute/bnd/version/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/configurable/Config.java
+ADDED RESOURCE OSGI-OPT/src/aQute/configurable/Configurable.java
+ADDED RESOURCE OSGI-OPT/src/aQute/configurable/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/base64/Base64.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/base64/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/EnumerationIterator.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/ExtList.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/IteratorList.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/LineCollection.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/Logic.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/MultiMap.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/SortedList.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/collections/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/converter/Converter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/converter/TypeReference.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/converter/packageinfo
+MAJOR RESOURCE OSGI-OPT/src/aQute/lib/deployer/FileRepo.java
+ REMOVED SHA 43016599CDFB8D16479E06DBADA03D8AF32783F1
+ ADDED SHA C447FBEB09414CF0ECF5508BA06A649DC9DD34FD
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/deployer/ProjectRepo.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/deployer/RDImpl.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/deployer/RepoDeployer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/filter/Filter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/filter/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/Arguments.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/CommandLine.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/CommandLineMessages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/Description.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/OptionArgument.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/Options.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/getopt/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/hex/Hex.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/hex/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/io/IO.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/io/IOConstants.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/io/LimitedInputStream.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/io/packageinfo
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/jardiff/Diff.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/ArrayHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/BooleanHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/ByteArrayHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/CharacterHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/CollectionHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/DateHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/Decoder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/Encoder.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/EnumHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/FileHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/Handler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/JSONCodec.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/MapHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/NumberHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/ObjectHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/SpecialHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/StringHandler.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/json/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/justif/Justif.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/justif/Table.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/justif/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/markdown/MarkdownFormatter.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/About.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Analyzer.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Builder.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Clazz.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Constants.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/EmbeddedResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/FileResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Instruction.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Jar.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/JarResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Macro.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/OpCodes.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Plugin.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/PreprocessResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Processor.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Resource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/URLResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/Verifier.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/ZipResource.java
+REMOVED RESOURCE OSGI-OPT/src/aQute/lib/osgi/eclipse/EclipseClasspath.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/persistentmap/PersistentMap.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/persistentmap/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/settings/PasswordCryptor.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/settings/Settings.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/settings/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/strings/Strings.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/strings/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/tag/Tag.java
+ADDED RESOURCE OSGI-OPT/src/aQute/lib/tag/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/classdump/ClassDumper.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/classdump/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/command/Command.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/command/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Crypto.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Digest.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Digester.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Key.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/MD5.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/RSA.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/SHA1.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/SHA256.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Signer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/Verifier.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/cryptography/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filelock/DirectoryLock.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filelock/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/AndFilter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/Filter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/LiteralFilter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/NotFilter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/Operator.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/OrFilter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/SimpleFilter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/filters/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/forker/Forker.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/forker/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/generics/Create.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/generics/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/glob/Glob.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/glob/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/map/MAP.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/map/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/qtokens/QuotedTokenizer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/qtokens/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/reporter/Message.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/reporter/ReporterAdapter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/reporter/ReporterMessages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/reporter/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/sed/Domain.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/sed/Replacer.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/sed/ReplacerAdapter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/sed/Sed.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/sed/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/tuple/ComparablePair.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/tuple/Pair.java
+ADDED RESOURCE OSGI-OPT/src/aQute/libg/tuple/packageinfo
+ADDED RESOURCE OSGI-OPT/src/aQute/service/reporter/Messages.java
+ADDED RESOURCE OSGI-OPT/src/aQute/service/reporter/Report.java
+ADDED RESOURCE OSGI-OPT/src/aQute/service/reporter/Reporter.java
+ADDED RESOURCE OSGI-OPT/src/aQute/service/reporter/packageinfo
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Capability.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Namespace.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Requirement.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Resource.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Wire.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/Wiring.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/package-info.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/resource/packageinfo
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/bindex/BundleIndexer.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/bindex/packageinfo
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/Activate.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/Component.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ConfigurationPolicy.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/Deactivate.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/LookupReference.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/Modified.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/Reference.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ReferenceCardinality.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ReferencePolicy.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ReferencePolicyOption.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ReferenceScope.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/ServiceScope.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/package-info.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/component/annotations/packageinfo
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/Coordination.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/CoordinationException.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/CoordinationPermission.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/Coordinator.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/Participant.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/package-info.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/coordinator/packageinfo
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/repository/ContentNamespace.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/repository/Repository.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/repository/RepositoryContent.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/repository/package-info.java
+ADDED RESOURCE OSGI-OPT/src/org/osgi/service/repository/packageinfo
+ADDED RESOURCE aQute/bnd/annotation/ConsumerType.class
+MAJOR RESOURCE aQute/bnd/annotation/Export.class
+ ADDED SHA 1143580202AA1EED607C3B44C18C4FDDFF807556
+ REMOVED SHA 2BFAD4181827144A0B7BF70F1B50AFFFD1486DB0
+REMOVED RESOURCE aQute/bnd/annotation/Import.class
+REMOVED RESOURCE aQute/bnd/annotation/Pair.class
+ADDED RESOURCE aQute/bnd/annotation/ProviderType.class
+ADDED RESOURCE aQute/bnd/annotation/Version.class
+ADDED RESOURCE aQute/bnd/annotation/component/Activate.class
+ADDED RESOURCE aQute/bnd/annotation/component/Component.class
+ADDED RESOURCE aQute/bnd/annotation/component/ConfigurationPolicy.class
+ADDED RESOURCE aQute/bnd/annotation/component/Deactivate.class
+ADDED RESOURCE aQute/bnd/annotation/component/Modified.class
+ADDED RESOURCE aQute/bnd/annotation/component/Reference.class
+ADDED RESOURCE aQute/bnd/annotation/component/packageinfo
+ADDED RESOURCE aQute/bnd/annotation/headers/About.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleCategory.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleContributors.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleCopyright.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleDevelopers.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleDocURL.class
+ADDED RESOURCE aQute/bnd/annotation/headers/BundleLicense.class
+ADDED RESOURCE aQute/bnd/annotation/headers/Category.class
+ADDED RESOURCE aQute/bnd/annotation/headers/ProvideCapability.class
+ADDED RESOURCE aQute/bnd/annotation/headers/RequireCapability.class
+ADDED RESOURCE aQute/bnd/annotation/headers/Resolution.class
+ADDED RESOURCE aQute/bnd/annotation/headers/packageinfo
+ADDED RESOURCE aQute/bnd/annotation/licenses/ASL_2_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/BSD_2_Clause.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/BSD_3_Clause.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/CDDL_1_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/CPL_1_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/EPL_1_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/GPL_2_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/GPL_3_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/LGPL_2_1.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/MIT_1_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/MPL_2_0.class
+ADDED RESOURCE aQute/bnd/annotation/licenses/packageinfo
+ADDED RESOURCE aQute/bnd/annotation/metatype/Configurable$ConfigurableHandler.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/Configurable.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/Meta$AD.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/Meta$OCD.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/Meta$Type.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/Meta.class
+ADDED RESOURCE aQute/bnd/annotation/metatype/packageinfo
+REMOVED RESOURCE aQute/bnd/annotation/package-info.class
+ADDED RESOURCE aQute/bnd/annotation/packageinfo
+ADDED RESOURCE aQute/bnd/ant/AntMessages.class
+MAJOR RESOURCE aQute/bnd/ant/BaseTask.class
+ REMOVED SHA 51D09D3D464164F6ADC6362CA1B27B38DFFF7775
+ ADDED SHA B4D7F65A6C5B0945D0BBDD1AD45C628C69945A49
+MAJOR RESOURCE aQute/bnd/ant/BndTask.class
+ ADDED SHA 2B9158162896486E31EF06E4EAADE45EC4F928BD
+ REMOVED SHA 3D24F9D832A2CFCE08FB5B63A0787BF8D8B9C08F
+ADDED RESOURCE aQute/bnd/ant/ConsoleProgress$1.class
+ADDED RESOURCE aQute/bnd/ant/ConsoleProgress.class
+MAJOR RESOURCE aQute/bnd/ant/DeployTask.class
+ REMOVED SHA 52741E8FA833A6F5770C2E07C44926B51AA03FF4
+ ADDED SHA B1FD3738343C5BDDEDCD61C8F16C82151F1E5C38
+MAJOR RESOURCE aQute/bnd/ant/EclipseTask.class
+ REMOVED SHA 1EDD3A0BD19408E75550B699EFD8E605175A966C
+ ADDED SHA 3BAD8CC8DB9F4C3AD5D0BBFF0CA4471FBB1DC27A
+MAJOR RESOURCE aQute/bnd/ant/ExpandPropertiesTask.class
+ ADDED SHA 27A2EFF363C15BAF95BBF4FE0F4511F082CC89D7
+ REMOVED SHA 2EFC6B42F01F13BFACB93F0D794547749A2392BF
+ADDED RESOURCE aQute/bnd/ant/PackageTask.class
+MAJOR RESOURCE aQute/bnd/ant/PrepareTask.class
+ REMOVED SHA 54FE708015DC152FBA2AC912C5F43823F2297E11
+ ADDED SHA 605DCAD147113525F3FA93F47FC598AC49B35266
+ADDED RESOURCE aQute/bnd/ant/ProjectBuildOrderTask.class
+MAJOR RESOURCE aQute/bnd/ant/ProjectTask.class
+ REMOVED SHA B99801A0059530E09B4C378C6BFB19F7EC51FC87
+ ADDED SHA C80B65108386F8484624573016EB7B170743DD5E
+ADDED RESOURCE aQute/bnd/ant/ReleaseTask.class
+ADDED RESOURCE aQute/bnd/ant/RunconfigToDistributionTask$JarFileFilter.class
+ADDED RESOURCE aQute/bnd/ant/RunconfigToDistributionTask$NonTestProjectFileFilter.class
+ADDED RESOURCE aQute/bnd/ant/RunconfigToDistributionTask.class
+ADDED RESOURCE aQute/bnd/ant/TestTask.class
+MAJOR RESOURCE aQute/bnd/ant/WrapTask.class
+ REMOVED SHA 1B2451001E019453C391ADD866B2C6F447BB8634
+ ADDED SHA F7EF8607FD423257913E692044478BA62CBC23AA
+MAJOR RESOURCE aQute/bnd/ant/taskdef.properties
+ REMOVED SHA 1F5BB944D17010889C822CB49ACD790CA0B4BEAA
+ ADDED SHA BB9108C257261825D22A7E893516293707C87E20
+MAJOR RESOURCE aQute/bnd/build/CircularDependencyException.class
+ REMOVED SHA 76252D29BAB7D6C9EEE9CA30D7C487A214974E40
+ ADDED SHA D4B16B74592DDA7F7E3DC794FD2FEB062EFB2B45
+MAJOR RESOURCE aQute/bnd/build/Container$TYPE.class
+ ADDED SHA 098B23F977005C8CD012084BF095FB6F0A4EA49F
+ REMOVED SHA AE3A226139BCF586C92B7003D1961DA741CA1738
+MAJOR RESOURCE aQute/bnd/build/Container.class
+ REMOVED SHA 8458A19DB2BB531949466ED9DB5B72E310BAC81D
+ ADDED SHA D196DA5CE6E0FF6D680D279C3C1017656BB9A579
+ADDED RESOURCE aQute/bnd/build/DownloadBlocker$Stage.class
+ADDED RESOURCE aQute/bnd/build/DownloadBlocker.class
+ADDED RESOURCE aQute/bnd/build/ErrorDetails.class
+REMOVED RESOURCE aQute/bnd/build/Framework$FilterLoader.class
+REMOVED RESOURCE aQute/bnd/build/Framework.class
+ADDED RESOURCE aQute/bnd/build/JUnitLauncher.class
+ADDED RESOURCE aQute/bnd/build/Project$1.class
+ADDED RESOURCE aQute/bnd/build/Project$2.class
+MAJOR RESOURCE aQute/bnd/build/Project.class
+ ADDED SHA 6B8BC68C0CA5FB105A03813147E09DC02C236327
+ REMOVED SHA C5F1CBF529FFFBE114BB09F8D0609B586E61103A
+MAJOR RESOURCE aQute/bnd/build/ProjectBuilder.class
+ REMOVED SHA 2E8690B06114FC5ECF967371D0A9F92FA64D1B35
+ ADDED SHA 6997CF482327EECA249D215F9522CD051FB13952
+ADDED RESOURCE aQute/bnd/build/ProjectLauncher$1.class
+ADDED RESOURCE aQute/bnd/build/ProjectLauncher$NotificationListener.class
+ADDED RESOURCE aQute/bnd/build/ProjectLauncher$NotificationType.class
+ADDED RESOURCE aQute/bnd/build/ProjectLauncher.class
+ADDED RESOURCE aQute/bnd/build/ProjectMessages.class
+ADDED RESOURCE aQute/bnd/build/ProjectTester.class
+ADDED RESOURCE aQute/bnd/build/ReflectAction.class
+ADDED RESOURCE aQute/bnd/build/ResolverMode.class
+ADDED RESOURCE aQute/bnd/build/Run.class
+ADDED RESOURCE aQute/bnd/build/ScriptAction.class
+ADDED RESOURCE aQute/bnd/build/Workspace$CachedFileRepo.class
+MAJOR RESOURCE aQute/bnd/build/Workspace.class
+ ADDED SHA 53261A511EDC0A6939C7CC784F2929BE2D1A233C
+ REMOVED SHA C1E463E37C42CB254EC9EE5AFDB456108F01B263
+ADDED RESOURCE aQute/bnd/build/WorkspaceRepository.class
+ADDED RESOURCE aQute/bnd/build/defaults.bnd
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$1.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$2.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$3.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$4.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$5.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel$6.class
+ADDED RESOURCE aQute/bnd/build/model/BndEditModel.class
+ADDED RESOURCE aQute/bnd/build/model/EE.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/ComponentSvcReference.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/ExportedPackage.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/HeaderClause.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/ImportPattern.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/ServiceComponent.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/VersionedClause.class
+ADDED RESOURCE aQute/bnd/build/model/clauses/packageinfo
+ADDED RESOURCE aQute/bnd/build/model/conversions/ClauseListConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/CollectionFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/Converter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/DefaultBooleanFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/DefaultFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/EEConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/EEFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/EnumConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/EnumFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/HeaderClauseConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/HeaderClauseFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/HeaderClauseListConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/MapFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/NoopConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/PropertiesConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/PropertiesEntryFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/RequirementFormatter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/RequirementListConverter$1.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/RequirementListConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/SimpleListConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/StringEntryConverter.class
+ADDED RESOURCE aQute/bnd/build/model/conversions/VersionedClauseConverter.class
+ADDED RESOURCE aQute/bnd/build/model/packageinfo
+ADDED RESOURCE aQute/bnd/build/packageinfo
+REMOVED RESOURCE aQute/bnd/classpath/BndContainer.class
+REMOVED RESOURCE aQute/bnd/classpath/BndContainerInitializer.class
+REMOVED RESOURCE aQute/bnd/classpath/BndContainerPage$1.class
+REMOVED RESOURCE aQute/bnd/classpath/BndContainerPage$2.class
+REMOVED RESOURCE aQute/bnd/classpath/BndContainerPage$3.class
+REMOVED RESOURCE aQute/bnd/classpath/BndContainerPage.class
+REMOVED RESOURCE aQute/bnd/classpath/ModelListener.class
+REMOVED RESOURCE aQute/bnd/classpath/messages.properties
+ADDED RESOURCE aQute/bnd/compatibility/Access.class
+ADDED RESOURCE aQute/bnd/compatibility/GenericParameter.class
+ADDED RESOURCE aQute/bnd/compatibility/GenericType$GenericArray.class
+ADDED RESOURCE aQute/bnd/compatibility/GenericType$GenericWildcard.class
+ADDED RESOURCE aQute/bnd/compatibility/GenericType.class
+ADDED RESOURCE aQute/bnd/compatibility/Kind.class
+ADDED RESOURCE aQute/bnd/compatibility/ParseSignatureBuilder$1.class
+ADDED RESOURCE aQute/bnd/compatibility/ParseSignatureBuilder.class
+ADDED RESOURCE aQute/bnd/compatibility/RuntimeSignatureBuilder.class
+ADDED RESOURCE aQute/bnd/compatibility/Scope.class
+ADDED RESOURCE aQute/bnd/compatibility/Signatures$Rover.class
+ADDED RESOURCE aQute/bnd/compatibility/Signatures.class
+ADDED RESOURCE aQute/bnd/component/AnnotationReader$1.class
+ADDED RESOURCE aQute/bnd/component/AnnotationReader.class
+ADDED RESOURCE aQute/bnd/component/ComponentDef.class
+ADDED RESOURCE aQute/bnd/component/DSAnnotations.class
+ADDED RESOURCE aQute/bnd/component/HeaderReader$1.class
+ADDED RESOURCE aQute/bnd/component/HeaderReader.class
+ADDED RESOURCE aQute/bnd/component/ReferenceDef.class
+ADDED RESOURCE aQute/bnd/component/TagResource.class
+ADDED RESOURCE aQute/bnd/component/error/DeclarativeServicesAnnotationError$ErrorType.class
+ADDED RESOURCE aQute/bnd/component/error/DeclarativeServicesAnnotationError.class
+ADDED RESOURCE aQute/bnd/component/error/packageinfo
+ADDED RESOURCE aQute/bnd/differ/Baseline$1.class
+ADDED RESOURCE aQute/bnd/differ/Baseline$BundleInfo.class
+ADDED RESOURCE aQute/bnd/differ/Baseline$Info.class
+ADDED RESOURCE aQute/bnd/differ/Baseline.class
+ADDED RESOURCE aQute/bnd/differ/DiffImpl.class
+ADDED RESOURCE aQute/bnd/differ/DiffPluginImpl.class
+ADDED RESOURCE aQute/bnd/differ/Element.class
+ADDED RESOURCE aQute/bnd/differ/JavaElement$1.class
+ADDED RESOURCE aQute/bnd/differ/JavaElement.class
+ADDED RESOURCE aQute/bnd/differ/RepositoryElement.class
+ADDED RESOURCE aQute/bnd/differ/packageinfo
+ADDED RESOURCE aQute/bnd/enroute/commands/EnrouteCommand$WorkspaceOptions.class
+ADDED RESOURCE aQute/bnd/enroute/commands/EnrouteCommand.class
+ADDED RESOURCE aQute/bnd/enroute/commands/EnrouteOptions.class
+ADDED RESOURCE aQute/bnd/filerepo/FileRepo$1.class
+ADDED RESOURCE aQute/bnd/filerepo/FileRepo$2.class
+ADDED RESOURCE aQute/bnd/filerepo/FileRepo$3.class
+ADDED RESOURCE aQute/bnd/filerepo/FileRepo.class
+ADDED RESOURCE aQute/bnd/filerepo/packageinfo
+ADDED RESOURCE aQute/bnd/gradle/BndPlugin.gradle
+ADDED RESOURCE aQute/bnd/header/Attrs$1.class
+ADDED RESOURCE aQute/bnd/header/Attrs$2.class
+ADDED RESOURCE aQute/bnd/header/Attrs$3.class
+ADDED RESOURCE aQute/bnd/header/Attrs$4.class
+ADDED RESOURCE aQute/bnd/header/Attrs$5.class
+ADDED RESOURCE aQute/bnd/header/Attrs$6.class
+ADDED RESOURCE aQute/bnd/header/Attrs$7.class
+ADDED RESOURCE aQute/bnd/header/Attrs$8.class
+ADDED RESOURCE aQute/bnd/header/Attrs$DataType.class
+ADDED RESOURCE aQute/bnd/header/Attrs$Type.class
+ADDED RESOURCE aQute/bnd/header/Attrs.class
+ADDED RESOURCE aQute/bnd/header/OSGiHeader.class
+ADDED RESOURCE aQute/bnd/header/Parameters.class
+ADDED RESOURCE aQute/bnd/header/packageinfo
+ADDED RESOURCE aQute/bnd/help/Syntax.class
+ADDED RESOURCE aQute/bnd/help/Warnings.class
+ADDED RESOURCE aQute/bnd/help/changed.txt
+ADDED RESOURCE aQute/bnd/help/packageinfo
+ADDED RESOURCE aQute/bnd/help/syntax.properties
+ADDED RESOURCE aQute/bnd/help/syntax.xml
+ADDED RESOURCE aQute/bnd/indexer/MimeType.class
+ADDED RESOURCE aQute/bnd/indexer/NOTE
+ADDED RESOURCE aQute/bnd/indexer/Namespaces.class
+ADDED RESOURCE aQute/bnd/indexer/RepoIndex$IndexResult.class
+ADDED RESOURCE aQute/bnd/indexer/RepoIndex.class
+ADDED RESOURCE aQute/bnd/indexer/ResourceAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/BlueprintAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/BundleAnalyzer$1.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/BundleAnalyzer$2.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/BundleAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/EE$Segment.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/EE.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/KnownBundleAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/NOTE
+ADDED RESOURCE aQute/bnd/indexer/analyzers/OSGiFrameworkAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/SCRAnalyzer$SCRContentHandler.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/SCRAnalyzer.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/Yield.class
+ADDED RESOURCE aQute/bnd/indexer/analyzers/known-bundles.properties
+REMOVED RESOURCE aQute/bnd/jareditor/JarConfiguration.class
+REMOVED RESOURCE aQute/bnd/jareditor/JarDocumentProvider.class
+REMOVED RESOURCE aQute/bnd/jareditor/JarEditor.class
+REMOVED RESOURCE aQute/bnd/junit/OSGiArgumentsTab.class
+REMOVED RESOURCE aQute/bnd/junit/OSGiJUnitLaunchShortcut.class
+REMOVED RESOURCE aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.class
+REMOVED RESOURCE aQute/bnd/junit/OSGiJUnitTabGroup.class
+REMOVED RESOURCE aQute/bnd/launch/LaunchDelegate.class
+REMOVED RESOURCE aQute/bnd/launch/LaunchTabGroup.class
+REMOVED RESOURCE aQute/bnd/launch/Shortcut.class
+ADDED RESOURCE aQute/bnd/main/BaselineCommands$1.class
+ADDED RESOURCE aQute/bnd/main/BaselineCommands$PSpec.class
+ADDED RESOURCE aQute/bnd/main/BaselineCommands$baseLineOptions.class
+ADDED RESOURCE aQute/bnd/main/BaselineCommands$schemaOptions.class
+ADDED RESOURCE aQute/bnd/main/BaselineCommands.class
+ADDED RESOURCE aQute/bnd/main/BndMessages.class
+ADDED RESOURCE aQute/bnd/main/DiffCommand$diffOptions.class
+ADDED RESOURCE aQute/bnd/main/DiffCommand.class
+ADDED RESOURCE aQute/bnd/main/PatchCommand$applyOptions.class
+ADDED RESOURCE aQute/bnd/main/PatchCommand$createOptions.class
+ADDED RESOURCE aQute/bnd/main/PatchCommand.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$1Spec.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$CopyOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$RefreshOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$VersionsOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$diffOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$getOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$listOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$putOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$repoOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand$reposOptions.class
+ADDED RESOURCE aQute/bnd/main/RepoCommand.class
+ADDED RESOURCE aQute/bnd/main/bnd$1.class
+ADDED RESOURCE aQute/bnd/main/bnd$2.class
+ADDED RESOURCE aQute/bnd/main/bnd$ActionOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$Alg.class
+ADDED RESOURCE aQute/bnd/main/bnd$All.class
+ADDED RESOURCE aQute/bnd/main/bnd$BootstrapOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$Bsn2UrlOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$ChangesOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$EEOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$FindOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$MergeOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$ParallelBuildOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$bndOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$buildoptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$buildxOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$bumpoptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$cleanOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$convertOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$createOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$debugOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$deliverableOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$dooptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$eclipseOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$extractOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$grepOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$hashOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$infoOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$macroOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$packageOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$patchOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$printOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$projectOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$releaseOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$runOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$runtestsOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$selectOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$settingOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$sourceOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$syntaxOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$testOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$typeOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$verifyOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$versionOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$viewOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$wrapOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd$xrefOptions.class
+ADDED RESOURCE aQute/bnd/main/bnd-completion.bash
+MAJOR RESOURCE aQute/bnd/main/bnd.class
+ REMOVED SHA EC8E2F8B6765E64C5D418653DC3871A6DA71844F
+ ADDED SHA FF3A6531C0DD6E4E5CD3D729560D599EB929AF89
+ADDED RESOURCE aQute/bnd/main/component.xslt
+ADDED RESOURCE aQute/bnd/main/maven-settings.xsl
+MAJOR RESOURCE aQute/bnd/main/packageinfo
+ REMOVED SHA 4CBF1AE09B541F925C0D6BCBB8684EF85A0B8373
+ ADDED SHA D6E9A3759405E18A266202C40D9AA4EBA6F83A87
+ADDED RESOURCE aQute/bnd/main/schema.xsl
+ADDED RESOURCE aQute/bnd/main/testreport.xsl
+MAJOR RESOURCE aQute/bnd/make/Make.class
+ REMOVED SHA C450A93D92842FFF73F919416D67D8566529ECD2
+ ADDED SHA F248C429BED7224A1B11ADAB230FF812487D41E2
+MAJOR RESOURCE aQute/bnd/make/MakeBnd.class
+ ADDED SHA B3A3364451F9DC3A4E091C47EA9D85E345B6F6F1
+ REMOVED SHA F255AC80281C5577080A25A0E0F2C1CEF36C53E9
+MAJOR RESOURCE aQute/bnd/make/MakeCopy.class
+ REMOVED SHA 1B9DD05EB5FF75A176E39BA6B3D23D8FA1610953
+ ADDED SHA C99F91A71B005FBA46F74B265A12475BA7B056AC
+ADDED RESOURCE aQute/bnd/make/calltree/CalltreeResource$1.class
+ADDED RESOURCE aQute/bnd/make/calltree/CalltreeResource$2.class
+ADDED RESOURCE aQute/bnd/make/calltree/CalltreeResource.class
+ADDED RESOURCE aQute/bnd/make/component/ComponentAnnotationReader.class
+ADDED RESOURCE aQute/bnd/make/component/ServiceComponent$ComponentMaker.class
+ADDED RESOURCE aQute/bnd/make/component/ServiceComponent.class
+ADDED RESOURCE aQute/bnd/make/coverage/Coverage$1.class
+ADDED RESOURCE aQute/bnd/make/coverage/Coverage$2.class
+ADDED RESOURCE aQute/bnd/make/coverage/Coverage$3.class
+ADDED RESOURCE aQute/bnd/make/coverage/Coverage.class
+ADDED RESOURCE aQute/bnd/make/coverage/CoverageResource.class
+ADDED RESOURCE aQute/bnd/make/metatype/MetaTypeReader$1.class
+ADDED RESOURCE aQute/bnd/make/metatype/MetaTypeReader$Find.class
+ADDED RESOURCE aQute/bnd/make/metatype/MetaTypeReader.class
+ADDED RESOURCE aQute/bnd/make/metatype/MetatypePlugin.class
+ADDED RESOURCE aQute/bnd/maven/BsnToMavenPath.class
+ADDED RESOURCE aQute/bnd/maven/MavenCommand.class
+ADDED RESOURCE aQute/bnd/maven/MavenDependencyGraph$Artifact.class
+ADDED RESOURCE aQute/bnd/maven/MavenDependencyGraph$Scope.class
+ADDED RESOURCE aQute/bnd/maven/MavenDependencyGraph.class
+ADDED RESOURCE aQute/bnd/maven/MavenDeploy.class
+ADDED RESOURCE aQute/bnd/maven/MavenDeployCmd.class
+ADDED RESOURCE aQute/bnd/maven/MavenGroup.class
+ADDED RESOURCE aQute/bnd/maven/MavenRepository.class
+ADDED RESOURCE aQute/bnd/maven/PomFromManifest.class
+ADDED RESOURCE aQute/bnd/maven/PomParser.class
+ADDED RESOURCE aQute/bnd/maven/PomResource.class
+ADDED RESOURCE aQute/bnd/maven/support/CachedPom.class
+ADDED RESOURCE aQute/bnd/maven/support/Maven.class
+ADDED RESOURCE aQute/bnd/maven/support/MavenEntry$1.class
+ADDED RESOURCE aQute/bnd/maven/support/MavenEntry$2.class
+ADDED RESOURCE aQute/bnd/maven/support/MavenEntry.class
+ADDED RESOURCE aQute/bnd/maven/support/MavenRemoteRepository.class
+ADDED RESOURCE aQute/bnd/maven/support/Pom$Dependency.class
+ADDED RESOURCE aQute/bnd/maven/support/Pom$Rover.class
+ADDED RESOURCE aQute/bnd/maven/support/Pom$Scope.class
+ADDED RESOURCE aQute/bnd/maven/support/Pom.class
+ADDED RESOURCE aQute/bnd/maven/support/ProjectPom$Rover.class
+ADDED RESOURCE aQute/bnd/maven/support/ProjectPom.class
+ADDED RESOURCE aQute/bnd/maven/support/Repo.class
+ADDED RESOURCE aQute/bnd/maven/support/packageinfo
+ADDED RESOURCE aQute/bnd/obr/OBRFragment.class
+ADDED RESOURCE aQute/bnd/osgi/About.class
+ADDED RESOURCE aQute/bnd/osgi/AbstractResource.class
+ADDED RESOURCE aQute/bnd/osgi/Analyzer$1.class
+ADDED RESOURCE aQute/bnd/osgi/Analyzer$2.class
+ADDED RESOURCE aQute/bnd/osgi/Analyzer.class
+ADDED RESOURCE aQute/bnd/osgi/AnalyzerMessages.class
+ADDED RESOURCE aQute/bnd/osgi/Annotation.class
+ADDED RESOURCE aQute/bnd/osgi/AnnotationHeaders.class
+ADDED RESOURCE aQute/bnd/osgi/Builder.class
+ADDED RESOURCE aQute/bnd/osgi/BundleId.class
+ADDED RESOURCE aQute/bnd/osgi/ClassDataCollector.class
+ADDED RESOURCE aQute/bnd/osgi/ClassDataCollectors.class
+ADDED RESOURCE aQute/bnd/osgi/Classpath$ClassVisitor.class
+ADDED RESOURCE aQute/bnd/osgi/Classpath.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$1.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$Assoc.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$ClassConstant.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$Def.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$FieldDef.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$JAVA$1.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$JAVA.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$MethodDef.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$QUERY.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz$TypeDef.class
+ADDED RESOURCE aQute/bnd/osgi/Clazz.class
+ADDED RESOURCE aQute/bnd/osgi/CombinedResource$1.class
+ADDED RESOURCE aQute/bnd/osgi/CombinedResource.class
+ADDED RESOURCE aQute/bnd/osgi/CommandResource.class
+ADDED RESOURCE aQute/bnd/osgi/Constants.class
+ADDED RESOURCE aQute/bnd/osgi/Contracts$Contract.class
+ADDED RESOURCE aQute/bnd/osgi/Contracts.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$ArrayRef.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$ConcreteRef.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$Descriptor.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$PackageRef.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$Signature.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$SignatureType.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors$TypeRef.class
+ADDED RESOURCE aQute/bnd/osgi/Descriptors.class
+ADDED RESOURCE aQute/bnd/osgi/Domain$1$1.class
+ADDED RESOURCE aQute/bnd/osgi/Domain$1.class
+ADDED RESOURCE aQute/bnd/osgi/Domain$2$1.class
+ADDED RESOURCE aQute/bnd/osgi/Domain$2.class
+ADDED RESOURCE aQute/bnd/osgi/Domain$3.class
+ADDED RESOURCE aQute/bnd/osgi/Domain.class
+ADDED RESOURCE aQute/bnd/osgi/EmbeddedResource.class
+ADDED RESOURCE aQute/bnd/osgi/FileResource.class
+ADDED RESOURCE aQute/bnd/osgi/Instruction$Filter.class
+ADDED RESOURCE aQute/bnd/osgi/Instruction.class
+ADDED RESOURCE aQute/bnd/osgi/Instructions.class
+ADDED RESOURCE aQute/bnd/osgi/Jar$Compression.class
+ADDED RESOURCE aQute/bnd/osgi/Jar.class
+ADDED RESOURCE aQute/bnd/osgi/JarResource.class
+ADDED RESOURCE aQute/bnd/osgi/Macro$1.class
+ADDED RESOURCE aQute/bnd/osgi/Macro$Link.class
+ADDED RESOURCE aQute/bnd/osgi/Macro.class
+ADDED RESOURCE aQute/bnd/osgi/OSInformation.class
+ADDED RESOURCE aQute/bnd/osgi/OpCodes.class
+ADDED RESOURCE aQute/bnd/osgi/Packages.class
+ADDED RESOURCE aQute/bnd/osgi/PreprocessResource.class
+ADDED RESOURCE aQute/bnd/osgi/Processor$1.class
+ADDED RESOURCE aQute/bnd/osgi/Processor$CL.class
+ADDED RESOURCE aQute/bnd/osgi/Processor$FileLine.class
+ADDED RESOURCE aQute/bnd/osgi/Processor$SetLocationImpl.class
+ADDED RESOURCE aQute/bnd/osgi/Processor.class
+ADDED RESOURCE aQute/bnd/osgi/Resource.class
+ADDED RESOURCE aQute/bnd/osgi/URLResource.class
+ADDED RESOURCE aQute/bnd/osgi/Verifier$ActivatorErrorType.class
+ADDED RESOURCE aQute/bnd/osgi/Verifier$BundleActivatorError.class
+ADDED RESOURCE aQute/bnd/osgi/Verifier$EE.class
+ADDED RESOURCE aQute/bnd/osgi/Verifier.class
+ADDED RESOURCE aQute/bnd/osgi/WriteResource$1.class
+ADDED RESOURCE aQute/bnd/osgi/WriteResource$CountingOutputStream.class
+ADDED RESOURCE aQute/bnd/osgi/WriteResource.class
+ADDED RESOURCE aQute/bnd/osgi/ZipResource.class
+ADDED RESOURCE aQute/bnd/osgi/bnd.info
+ADDED RESOURCE aQute/bnd/osgi/eclipse/EclipseClasspath.class
+ADDED RESOURCE aQute/bnd/osgi/packageinfo
+ADDED RESOURCE aQute/bnd/osgi/profiles-OpenJDK8.properties
+ADDED RESOURCE aQute/bnd/osgi/resource/CapReq$MODE.class
+ADDED RESOURCE aQute/bnd/osgi/resource/CapReq.class
+ADDED RESOURCE aQute/bnd/osgi/resource/CapReqBuilder.class
+ADDED RESOURCE aQute/bnd/osgi/resource/CapabilityImpl.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$1.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$And.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$ApproximateExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$BundleExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Expression$1.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Expression$2.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Expression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$HostExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$IdentityExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Not.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Op.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Or.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$PackageExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$PatternExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$RangeExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$Rover.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$SimpleExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$SubExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser$WithRangeExpression.class
+ADDED RESOURCE aQute/bnd/osgi/resource/FilterParser.class
+ADDED RESOURCE aQute/bnd/osgi/resource/Filters.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource$Attr.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource$DataType.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource$Namespace.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource$RC.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource$RCData.class
+ADDED RESOURCE aQute/bnd/osgi/resource/PersistentResource.class
+ADDED RESOURCE aQute/bnd/osgi/resource/RequirementImpl.class
+ADDED RESOURCE aQute/bnd/osgi/resource/ResourceBuilder.class
+ADDED RESOURCE aQute/bnd/osgi/resource/ResourceImpl.class
+ADDED RESOURCE aQute/bnd/osgi/resource/packageinfo
+REMOVED RESOURCE aQute/bnd/plugin/Activator$1.class
+REMOVED RESOURCE aQute/bnd/plugin/Activator$2.class
+REMOVED RESOURCE aQute/bnd/plugin/Activator$3.class
+REMOVED RESOURCE aQute/bnd/plugin/Activator.class
+REMOVED RESOURCE aQute/bnd/plugin/Central$1.class
+REMOVED RESOURCE aQute/bnd/plugin/Central.class
+REMOVED RESOURCE aQute/bnd/plugin/builder/BndBuilder$DeltaVisitor.class
+REMOVED RESOURCE aQute/bnd/plugin/builder/BndBuilder$ResourceVisitor.class
+REMOVED RESOURCE aQute/bnd/plugin/builder/BndBuilder.class
+REMOVED RESOURCE aQute/bnd/plugin/builder/BndNature.class
+REMOVED RESOURCE aQute/bnd/plugin/builder/ToggleNatureAction.class
+REMOVED RESOURCE aQute/bnd/plugin/popup/actions/InstallBundle.class
+REMOVED RESOURCE aQute/bnd/plugin/popup/actions/MakeBundle.class
+REMOVED RESOURCE aQute/bnd/plugin/popup/actions/VerifyBundle.class
+REMOVED RESOURCE aQute/bnd/plugin/popup/actions/WrapBundle.class
+ADDED RESOURCE aQute/bnd/properties/BadLocationException.class
+ADDED RESOURCE aQute/bnd/properties/CopyOnWriteTextStore$StringTextStore.class
+ADDED RESOURCE aQute/bnd/properties/CopyOnWriteTextStore.class
+ADDED RESOURCE aQute/bnd/properties/Document$DelimiterInfo.class
+ADDED RESOURCE aQute/bnd/properties/Document.class
+ADDED RESOURCE aQute/bnd/properties/GapTextStore.class
+ADDED RESOURCE aQute/bnd/properties/IDocument.class
+ADDED RESOURCE aQute/bnd/properties/IRegion.class
+ADDED RESOURCE aQute/bnd/properties/ITextStore.class
+ADDED RESOURCE aQute/bnd/properties/Line.class
+ADDED RESOURCE aQute/bnd/properties/LineTracker.class
+ADDED RESOURCE aQute/bnd/properties/LineType.class
+ADDED RESOURCE aQute/bnd/properties/PropertiesLineReader.class
+ADDED RESOURCE aQute/bnd/properties/Region.class
+ADDED RESOURCE aQute/bnd/properties/packageinfo
+ADDED RESOURCE aQute/bnd/resource/repository/ResourceDescriptorImpl.class
+ADDED RESOURCE aQute/bnd/resource/repository/ResourceRepositoryImpl$1.class
+ADDED RESOURCE aQute/bnd/resource/repository/ResourceRepositoryImpl$2.class
+ADDED RESOURCE aQute/bnd/resource/repository/ResourceRepositoryImpl$FileLayout.class
+ADDED RESOURCE aQute/bnd/resource/repository/ResourceRepositoryImpl.class
+ADDED RESOURCE aQute/bnd/service/Actionable.class
+MAJOR RESOURCE aQute/bnd/service/AnalyzerPlugin.class
+ REMOVED SHA 6174C960699993F1F85880FE856A711616BDE67E
+ ADDED SHA 627FCDE5472BC5042106CCF06CBD3080BEA7B30D
+ADDED RESOURCE aQute/bnd/service/BndListener.class
+ADDED RESOURCE aQute/bnd/service/CommandPlugin.class
+ADDED RESOURCE aQute/bnd/service/Compiler.class
+ADDED RESOURCE aQute/bnd/service/DependencyContributor.class
+ADDED RESOURCE aQute/bnd/service/Deploy.class
+ADDED RESOURCE aQute/bnd/service/EclipseJUnitTester.class
+ADDED RESOURCE aQute/bnd/service/IndexProvider.class
+ADDED RESOURCE aQute/bnd/service/LauncherPlugin.class
+MAJOR RESOURCE aQute/bnd/service/MakePlugin.class
+ REMOVED SHA 6CBEC0D0266E75F5B416D933C8DB96BAD3F11858
+ ADDED SHA 862ADC2AD2ED8E88CEE4F668F281B41C7883E53A
+ADDED RESOURCE aQute/bnd/service/Plugin.class
+ADDED RESOURCE aQute/bnd/service/Refreshable.class
+ADDED RESOURCE aQute/bnd/service/Registry.class
+ADDED RESOURCE aQute/bnd/service/RegistryDonePlugin.class
+ADDED RESOURCE aQute/bnd/service/RegistryPlugin.class
+ADDED RESOURCE aQute/bnd/service/RemoteRepositoryPlugin.class
+ADDED RESOURCE aQute/bnd/service/RepositoryListenerPlugin.class
+ADDED RESOURCE aQute/bnd/service/RepositoryPlugin$DownloadListener.class
+ADDED RESOURCE aQute/bnd/service/RepositoryPlugin$PutOptions.class
+ADDED RESOURCE aQute/bnd/service/RepositoryPlugin$PutResult.class
+MAJOR RESOURCE aQute/bnd/service/RepositoryPlugin.class
+ ADDED SHA 7865276EC971AD9FFACB3A296DECE9E4C9A4A03F
+ REMOVED SHA 957238133AA5116ABC5415641ACE3D087FFD44BE
+ADDED RESOURCE aQute/bnd/service/ResolutionPhase.class
+ADDED RESOURCE aQute/bnd/service/ResourceHandle$Location.class
+ADDED RESOURCE aQute/bnd/service/ResourceHandle.class
+ADDED RESOURCE aQute/bnd/service/Scripter.class
+ADDED RESOURCE aQute/bnd/service/SignerPlugin.class
+ADDED RESOURCE aQute/bnd/service/Strategy.class
+ADDED RESOURCE aQute/bnd/service/action/Action.class
+ADDED RESOURCE aQute/bnd/service/action/NamedAction.class
+ADDED RESOURCE aQute/bnd/service/action/packageinfo
+ADDED RESOURCE aQute/bnd/service/classparser/ClassParser.class
+ADDED RESOURCE aQute/bnd/service/classparser/packageinfo
+ADDED RESOURCE aQute/bnd/service/diff/Delta.class
+ADDED RESOURCE aQute/bnd/service/diff/Diff$Data.class
+ADDED RESOURCE aQute/bnd/service/diff/Diff$Ignore.class
+ADDED RESOURCE aQute/bnd/service/diff/Diff.class
+ADDED RESOURCE aQute/bnd/service/diff/Differ.class
+ADDED RESOURCE aQute/bnd/service/diff/Tree$Data.class
+ADDED RESOURCE aQute/bnd/service/diff/Tree.class
+ADDED RESOURCE aQute/bnd/service/diff/Type.class
+ADDED RESOURCE aQute/bnd/service/diff/packageinfo
+ADDED RESOURCE aQute/bnd/service/extension/ExtensionActivator.class
+ADDED RESOURCE aQute/bnd/service/extension/packageinfo
+ADDED RESOURCE aQute/bnd/service/packageinfo
+ADDED RESOURCE aQute/bnd/service/progress/ProgressPlugin$Task.class
+ADDED RESOURCE aQute/bnd/service/progress/ProgressPlugin.class
+ADDED RESOURCE aQute/bnd/service/progress/packageinfo
+ADDED RESOURCE aQute/bnd/service/repository/InfoRepository.class
+ADDED RESOURCE aQute/bnd/service/repository/MinimalRepository$Gestalt.class
+ADDED RESOURCE aQute/bnd/service/repository/MinimalRepository.class
+ADDED RESOURCE aQute/bnd/service/repository/Phase.class
+ADDED RESOURCE aQute/bnd/service/repository/RepositoryDigest.class
+ADDED RESOURCE aQute/bnd/service/repository/ResourceRepository$Listener.class
+ADDED RESOURCE aQute/bnd/service/repository/ResourceRepository$ResourceRepositoryEvent.class
+ADDED RESOURCE aQute/bnd/service/repository/ResourceRepository$TYPE.class
+ADDED RESOURCE aQute/bnd/service/repository/ResourceRepository.class
+ADDED RESOURCE aQute/bnd/service/repository/SearchableRepository$ResourceDescriptor.class
+ADDED RESOURCE aQute/bnd/service/repository/SearchableRepository.class
+ADDED RESOURCE aQute/bnd/service/repository/packageinfo
+ADDED RESOURCE aQute/bnd/service/resolve/hook/ResolverHook.class
+ADDED RESOURCE aQute/bnd/service/resolve/hook/packageinfo
+ADDED RESOURCE aQute/bnd/service/url/TaggedData.class
+ADDED RESOURCE aQute/bnd/service/url/URLConnectionHandler.class
+ADDED RESOURCE aQute/bnd/service/url/URLConnector.class
+ADDED RESOURCE aQute/bnd/service/url/packageinfo
+ADDED RESOURCE aQute/bnd/signing/JartoolSigner$1.class
+ADDED RESOURCE aQute/bnd/signing/JartoolSigner.class
+ADDED RESOURCE aQute/bnd/signing/Signer.class
+REMOVED RESOURCE aQute/bnd/test/ProjectLauncher$1.class
+REMOVED RESOURCE aQute/bnd/test/ProjectLauncher$Streamer.class
+REMOVED RESOURCE aQute/bnd/test/ProjectLauncher.class
+REMOVED RESOURCE aQute/bnd/test/aQute.runtime.jar
+ADDED RESOURCE aQute/bnd/testing/DSTestWiring$Component.class
+ADDED RESOURCE aQute/bnd/testing/DSTestWiring$Reference.class
+ADDED RESOURCE aQute/bnd/testing/DSTestWiring.class
+ADDED RESOURCE aQute/bnd/testing/TestingLog$1.class
+ADDED RESOURCE aQute/bnd/testing/TestingLog$Config.class
+ADDED RESOURCE aQute/bnd/testing/TestingLog.class
+ADDED RESOURCE aQute/bnd/testing/packageinfo
+ADDED RESOURCE aQute/bnd/url/BasicAuthentication.class
+ADDED RESOURCE aQute/bnd/url/BndAuthentication.class
+ADDED RESOURCE aQute/bnd/url/ConnectionSettings$Config.class
+ADDED RESOURCE aQute/bnd/url/ConnectionSettings.class
+ADDED RESOURCE aQute/bnd/url/DefaultURLConnectionHandler.class
+ADDED RESOURCE aQute/bnd/url/HttpsVerification$1.class
+ADDED RESOURCE aQute/bnd/url/HttpsVerification$2.class
+ADDED RESOURCE aQute/bnd/url/HttpsVerification.class
+ADDED RESOURCE aQute/bnd/url/MultiURLConnectionHandler.class
+ADDED RESOURCE aQute/bnd/url/packageinfo
+ADDED RESOURCE aQute/bnd/util/dto/DTO.class
+ADDED RESOURCE aQute/bnd/util/dto/packageinfo
+ADDED RESOURCE aQute/bnd/version/Version.class
+ADDED RESOURCE aQute/bnd/version/VersionRange.class
+ADDED RESOURCE aQute/bnd/version/packageinfo
+ADDED RESOURCE aQute/configurable/Config.class
+ADDED RESOURCE aQute/configurable/Configurable$ConfigurableHandler.class
+ADDED RESOURCE aQute/configurable/Configurable.class
+ADDED RESOURCE aQute/configurable/packageinfo
+ADDED RESOURCE aQute/lib/base64/Base64.class
+ADDED RESOURCE aQute/lib/base64/packageinfo
+ADDED RESOURCE aQute/lib/collections/EnumerationIterator.class
+ADDED RESOURCE aQute/lib/collections/ExtList.class
+ADDED RESOURCE aQute/lib/collections/IteratorList.class
+ADDED RESOURCE aQute/lib/collections/LineCollection.class
+ADDED RESOURCE aQute/lib/collections/Logic.class
+ADDED RESOURCE aQute/lib/collections/MultiMap$1.class
+ADDED RESOURCE aQute/lib/collections/MultiMap.class
+ADDED RESOURCE aQute/lib/collections/SortedList$1.class
+ADDED RESOURCE aQute/lib/collections/SortedList$It.class
+ADDED RESOURCE aQute/lib/collections/SortedList.class
+ADDED RESOURCE aQute/lib/collections/packageinfo
+ADDED RESOURCE aQute/lib/converter/Converter$1.class
+ADDED RESOURCE aQute/lib/converter/Converter$Hook.class
+ADDED RESOURCE aQute/lib/converter/Converter.class
+ADDED RESOURCE aQute/lib/converter/TypeReference.class
+ADDED RESOURCE aQute/lib/converter/packageinfo
+ADDED RESOURCE aQute/lib/deployer/FileRepo$1.class
+ADDED RESOURCE aQute/lib/deployer/FileRepo$2.class
+ADDED RESOURCE aQute/lib/deployer/FileRepo$3.class
+MAJOR RESOURCE aQute/lib/deployer/FileRepo.class
+ ADDED SHA 5AE445A1EFA3EA6F6BF2120F28AF029BC32DEFC1
+ REMOVED SHA DA9A1E483557668D4A3CD944C96DB236DE7920CB
+REMOVED RESOURCE aQute/lib/deployer/ProjectRepo.class
+ADDED RESOURCE aQute/lib/deployer/RDImpl.class
+REMOVED RESOURCE aQute/lib/deployer/RepoDeployer.class
+ADDED RESOURCE aQute/lib/filter/Filter$DictQuery.class
+ADDED RESOURCE aQute/lib/filter/Filter$MapQuery.class
+ADDED RESOURCE aQute/lib/filter/Filter$Query.class
+ADDED RESOURCE aQute/lib/filter/Filter.class
+ADDED RESOURCE aQute/lib/filter/packageinfo
+ADDED RESOURCE aQute/lib/getopt/Arguments.class
+ADDED RESOURCE aQute/lib/getopt/CommandLine$Option.class
+ADDED RESOURCE aQute/lib/getopt/CommandLine.class
+ADDED RESOURCE aQute/lib/getopt/CommandLineMessages.class
+ADDED RESOURCE aQute/lib/getopt/Description.class
+ADDED RESOURCE aQute/lib/getopt/OptionArgument.class
+ADDED RESOURCE aQute/lib/getopt/Options.class
+ADDED RESOURCE aQute/lib/getopt/packageinfo
+ADDED RESOURCE aQute/lib/hex/Hex.class
+ADDED RESOURCE aQute/lib/hex/packageinfo
+ADDED RESOURCE aQute/lib/io/IO$1.class
+ADDED RESOURCE aQute/lib/io/IO$2.class
+ADDED RESOURCE aQute/lib/io/IO.class
+ADDED RESOURCE aQute/lib/io/IOConstants.class
+ADDED RESOURCE aQute/lib/io/LimitedInputStream.class
+ADDED RESOURCE aQute/lib/io/packageinfo
+REMOVED RESOURCE aQute/lib/jardiff/Diff.class
+ADDED RESOURCE aQute/lib/json/ArrayHandler.class
+ADDED RESOURCE aQute/lib/json/BooleanHandler.class
+ADDED RESOURCE aQute/lib/json/ByteArrayHandler.class
+ADDED RESOURCE aQute/lib/json/CharacterHandler.class
+ADDED RESOURCE aQute/lib/json/CollectionHandler.class
+ADDED RESOURCE aQute/lib/json/DateHandler.class
+ADDED RESOURCE aQute/lib/json/Decoder.class
+ADDED RESOURCE aQute/lib/json/Encoder.class
+ADDED RESOURCE aQute/lib/json/EnumHandler.class
+ADDED RESOURCE aQute/lib/json/FileHandler.class
+ADDED RESOURCE aQute/lib/json/Handler.class
+ADDED RESOURCE aQute/lib/json/JSONCodec.class
+ADDED RESOURCE aQute/lib/json/MapHandler.class
+ADDED RESOURCE aQute/lib/json/NumberHandler.class
+ADDED RESOURCE aQute/lib/json/ObjectHandler$1.class
+ADDED RESOURCE aQute/lib/json/ObjectHandler.class
+ADDED RESOURCE aQute/lib/json/SpecialHandler.class
+ADDED RESOURCE aQute/lib/json/StringHandler.class
+ADDED RESOURCE aQute/lib/json/packageinfo
+ADDED RESOURCE aQute/lib/justif/Justif.class
+ADDED RESOURCE aQute/lib/justif/Table.class
+ADDED RESOURCE aQute/lib/justif/packageinfo
+ADDED RESOURCE aQute/lib/markdown/MarkdownFormatter.class
+REMOVED RESOURCE aQute/lib/osgi/About.class
+REMOVED RESOURCE aQute/lib/osgi/Analyzer.class
+REMOVED RESOURCE aQute/lib/osgi/Builder.class
+REMOVED RESOURCE aQute/lib/osgi/Clazz$Assoc.class
+REMOVED RESOURCE aQute/lib/osgi/Clazz$QUERY.class
+REMOVED RESOURCE aQute/lib/osgi/Clazz.class
+REMOVED RESOURCE aQute/lib/osgi/Constants.class
+REMOVED RESOURCE aQute/lib/osgi/EmbeddedResource.class
+REMOVED RESOURCE aQute/lib/osgi/FileResource.class
+REMOVED RESOURCE aQute/lib/osgi/Instruction.class
+REMOVED RESOURCE aQute/lib/osgi/Jar.class
+REMOVED RESOURCE aQute/lib/osgi/JarResource.class
+REMOVED RESOURCE aQute/lib/osgi/Macro$Link.class
+REMOVED RESOURCE aQute/lib/osgi/Macro.class
+REMOVED RESOURCE aQute/lib/osgi/OpCodes.class
+REMOVED RESOURCE aQute/lib/osgi/Plugin.class
+REMOVED RESOURCE aQute/lib/osgi/PreprocessResource.class
+REMOVED RESOURCE aQute/lib/osgi/Processor.class
+REMOVED RESOURCE aQute/lib/osgi/Resource.class
+REMOVED RESOURCE aQute/lib/osgi/URLResource.class
+REMOVED RESOURCE aQute/lib/osgi/Verifier.class
+REMOVED RESOURCE aQute/lib/osgi/ZipResource.class
+REMOVED RESOURCE aQute/lib/osgi/bnd.info
+REMOVED RESOURCE aQute/lib/osgi/eclipse/EclipseClasspath.class
+ADDED RESOURCE aQute/lib/persistentmap/PersistentMap$1$1$1.class
+ADDED RESOURCE aQute/lib/persistentmap/PersistentMap$1$1.class
+ADDED RESOURCE aQute/lib/persistentmap/PersistentMap$1.class
+ADDED RESOURCE aQute/lib/persistentmap/PersistentMap.class
+ADDED RESOURCE aQute/lib/persistentmap/packageinfo
+ADDED RESOURCE aQute/lib/settings/PasswordCryptor.class
+ADDED RESOURCE aQute/lib/settings/Settings$Data.class
+ADDED RESOURCE aQute/lib/settings/Settings.class
+ADDED RESOURCE aQute/lib/settings/packageinfo
+ADDED RESOURCE aQute/lib/strings/Strings.class
+ADDED RESOURCE aQute/lib/strings/packageinfo
+ADDED RESOURCE aQute/lib/tag/Tag.class
+ADDED RESOURCE aQute/lib/tag/packageinfo
+ADDED RESOURCE aQute/libg/classdump/ClassDumper$Assoc.class
+ADDED RESOURCE aQute/libg/classdump/ClassDumper.class
+ADDED RESOURCE aQute/libg/classdump/packageinfo
+ADDED RESOURCE aQute/libg/command/Command$1.class
+ADDED RESOURCE aQute/libg/command/Command$2.class
+ADDED RESOURCE aQute/libg/command/Command$3.class
+ADDED RESOURCE aQute/libg/command/Command$Collector.class
+ADDED RESOURCE aQute/libg/command/Command.class
+ADDED RESOURCE aQute/libg/command/packageinfo
+ADDED RESOURCE aQute/libg/cryptography/Crypto.class
+ADDED RESOURCE aQute/libg/cryptography/Digest.class
+ADDED RESOURCE aQute/libg/cryptography/Digester.class
+ADDED RESOURCE aQute/libg/cryptography/Key.class
+ADDED RESOURCE aQute/libg/cryptography/MD5$1.class
+ADDED RESOURCE aQute/libg/cryptography/MD5.class
+ADDED RESOURCE aQute/libg/cryptography/RSA.class
+ADDED RESOURCE aQute/libg/cryptography/SHA1$1.class
+ADDED RESOURCE aQute/libg/cryptography/SHA1.class
+ADDED RESOURCE aQute/libg/cryptography/SHA256$1.class
+ADDED RESOURCE aQute/libg/cryptography/SHA256.class
+ADDED RESOURCE aQute/libg/cryptography/Signer.class
+ADDED RESOURCE aQute/libg/cryptography/Verifier.class
+ADDED RESOURCE aQute/libg/cryptography/packageinfo
+ADDED RESOURCE aQute/libg/filelock/DirectoryLock.class
+ADDED RESOURCE aQute/libg/filelock/packageinfo
+ADDED RESOURCE aQute/libg/filters/AndFilter.class
+ADDED RESOURCE aQute/libg/filters/Filter.class
+ADDED RESOURCE aQute/libg/filters/LiteralFilter.class
+ADDED RESOURCE aQute/libg/filters/NotFilter.class
+ADDED RESOURCE aQute/libg/filters/Operator.class
+ADDED RESOURCE aQute/libg/filters/OrFilter.class
+ADDED RESOURCE aQute/libg/filters/SimpleFilter.class
+ADDED RESOURCE aQute/libg/filters/packageinfo
+ADDED RESOURCE aQute/libg/forker/Forker$Job.class
+ADDED RESOURCE aQute/libg/forker/Forker.class
+ADDED RESOURCE aQute/libg/forker/packageinfo
+ADDED RESOURCE aQute/libg/generics/Create.class
+ADDED RESOURCE aQute/libg/generics/packageinfo
+ADDED RESOURCE aQute/libg/glob/Glob.class
+ADDED RESOURCE aQute/libg/glob/packageinfo
+ADDED RESOURCE aQute/libg/map/MAP$MAPX.class
+ADDED RESOURCE aQute/libg/map/MAP.class
+ADDED RESOURCE aQute/libg/map/packageinfo
+ADDED RESOURCE aQute/libg/qtokens/QuotedTokenizer.class
+ADDED RESOURCE aQute/libg/qtokens/packageinfo
+ADDED RESOURCE aQute/libg/reporter/Message.class
+ADDED RESOURCE aQute/libg/reporter/ReporterAdapter$LocationImpl.class
+ADDED RESOURCE aQute/libg/reporter/ReporterAdapter.class
+ADDED RESOURCE aQute/libg/reporter/ReporterMessages$1.class
+ADDED RESOURCE aQute/libg/reporter/ReporterMessages$ERRORImpl.class
+ADDED RESOURCE aQute/libg/reporter/ReporterMessages$WARNINGImpl.class
+ADDED RESOURCE aQute/libg/reporter/ReporterMessages.class
+ADDED RESOURCE aQute/libg/reporter/packageinfo
+ADDED RESOURCE aQute/libg/sed/Domain.class
+ADDED RESOURCE aQute/libg/sed/Replacer.class
+ADDED RESOURCE aQute/libg/sed/ReplacerAdapter$1.class
+ADDED RESOURCE aQute/libg/sed/ReplacerAdapter$2.class
+ADDED RESOURCE aQute/libg/sed/ReplacerAdapter$3.class
+ADDED RESOURCE aQute/libg/sed/ReplacerAdapter$Link.class
+ADDED RESOURCE aQute/libg/sed/ReplacerAdapter.class
+ADDED RESOURCE aQute/libg/sed/Sed.class
+ADDED RESOURCE aQute/libg/sed/packageinfo
+ADDED RESOURCE aQute/libg/tuple/ComparablePair.class
+ADDED RESOURCE aQute/libg/tuple/Pair.class
+ADDED RESOURCE aQute/libg/tuple/packageinfo
+ADDED RESOURCE aQute/service/reporter/Messages$ERROR.class
+ADDED RESOURCE aQute/service/reporter/Messages$WARNING.class
+ADDED RESOURCE aQute/service/reporter/Messages.class
+ADDED RESOURCE aQute/service/reporter/Report$Location.class
+ADDED RESOURCE aQute/service/reporter/Report.class
+ADDED RESOURCE aQute/service/reporter/Reporter$SetLocation.class
+ADDED RESOURCE aQute/service/reporter/Reporter.class
+ADDED RESOURCE aQute/service/reporter/packageinfo
+ADDED RESOURCE embedded-repo.jar
+REMOVED RESOURCE maven-dependencies.txt
+ADDED RESOURCE org/osgi/resource/Capability.class
+ADDED RESOURCE org/osgi/resource/Namespace.class
+ADDED RESOURCE org/osgi/resource/Requirement.class
+ADDED RESOURCE org/osgi/resource/Resource.class
+ADDED RESOURCE org/osgi/resource/Wire.class
+ADDED RESOURCE org/osgi/resource/Wiring.class
+ADDED RESOURCE org/osgi/resource/package-info.class
+ADDED RESOURCE org/osgi/resource/packageinfo
+ADDED RESOURCE org/osgi/service/bindex/BundleIndexer.class
+ADDED RESOURCE org/osgi/service/bindex/packageinfo
+ADDED RESOURCE org/osgi/service/component/annotations/Activate.class
+ADDED RESOURCE org/osgi/service/component/annotations/Component.class
+ADDED RESOURCE org/osgi/service/component/annotations/ConfigurationPolicy.class
+ADDED RESOURCE org/osgi/service/component/annotations/Deactivate.class
+ADDED RESOURCE org/osgi/service/component/annotations/LookupReference.class
+ADDED RESOURCE org/osgi/service/component/annotations/Modified.class
+ADDED RESOURCE org/osgi/service/component/annotations/Reference.class
+ADDED RESOURCE org/osgi/service/component/annotations/ReferenceCardinality.class
+ADDED RESOURCE org/osgi/service/component/annotations/ReferencePolicy.class
+ADDED RESOURCE org/osgi/service/component/annotations/ReferencePolicyOption.class
+ADDED RESOURCE org/osgi/service/component/annotations/ReferenceScope.class
+ADDED RESOURCE org/osgi/service/component/annotations/ServiceScope.class
+ADDED RESOURCE org/osgi/service/component/annotations/package-info.class
+ADDED RESOURCE org/osgi/service/component/annotations/packageinfo
+ADDED RESOURCE org/osgi/service/coordinator/Coordination.class
+ADDED RESOURCE org/osgi/service/coordinator/CoordinationException.class
+ADDED RESOURCE org/osgi/service/coordinator/CoordinationPermission$1.class
+ADDED RESOURCE org/osgi/service/coordinator/CoordinationPermission.class
+ADDED RESOURCE org/osgi/service/coordinator/CoordinationPermissionCollection.class
+ADDED RESOURCE org/osgi/service/coordinator/Coordinator.class
+ADDED RESOURCE org/osgi/service/coordinator/Participant.class
+ADDED RESOURCE org/osgi/service/coordinator/SignerProperty.class
+ADDED RESOURCE org/osgi/service/coordinator/package-info.class
+ADDED RESOURCE org/osgi/service/coordinator/packageinfo
+ADDED RESOURCE org/osgi/service/repository/ContentNamespace.class
+ADDED RESOURCE org/osgi/service/repository/Repository.class
+ADDED RESOURCE org/osgi/service/repository/RepositoryContent.class
+ADDED RESOURCE org/osgi/service/repository/package-info.class
+ADDED RESOURCE org/osgi/service/repository/packageinfo
+REMOVED RESOURCE plugin.xml
+REMOVED RESOURCE pom.xml
+ADDED RESOURCE templates/enroute.zip
+ digest [options] <file...>
+[ -a --algorithm <alg>* ] Specify the algorithms[ -b --b64 ] Show base64 output[ -h --hex ] Show hex output (default)[ -n --name ] Show the file name[ -p --process ] Show process infobiz.aQute.bnd (master)$ bnd digest generated/biz.aQute.bnd.jar
+16B415286B53FA499BD7B2684A93924CA7C198C8
+ do [options] ...
+[ -f --force ] Force even when there are errors[ -o --output <string> ] The output filebiz.aQute.bnd (master)$ bnd do generated/biz.aQute.bnd.jar
+[MANIFEST biz.aQute.bnd]
+Bnd-LastModified 1404915822703
+Bundle-Copyright Copyright (c) aQute (2000, 2014). All Rights Reserved.
+Bundle-Description This command line utility is the Swiss army knife of OSGi. It provides you with a breadth
+Bundle-DocURL http://www.aQute.biz/Code/Bnd
+Bundle-License http://www.opensource.org/licenses/apache2.0.php; description="Apache License, Version 2.0"; link=http://www.apache.org/licenses/LICENSE-2.0.html
+Bundle-ManifestVersion 2
+Bundle-Name biz.aQute.bnd
+Bundle-SCM git://github.com/bndtools/bnd.git
+Bundle-SymbolicName biz.aQute.bnd
+Bundle-Vendor aQute SARL http://www.aQute.biz
+Bundle-Version 2.4.0.201407091423
+Conditional-Package aQute.libg.*,aQute.lib.*,aQute.configurable
+Created-By 1.8.0 (Oracle Corporation)
+Export-Package aQute.bnd.service;version="4.1.0";uses:="aQute.bnd.build,aQute.bnd.osgi,aQute.bnd.version,aQute.service.reporter",aQute.bnd.service.action;version="2.0.0";uses:="aQute.bnd.build",aQute.bnd.service.classparser;version="1.0";uses:="aQute.bnd.osgi",aQute.bnd.service.diff;version="1.0";uses:="aQute.bnd.osgi",aQute.bnd.service.extension;version="1.0";uses:="aQute.bnd.build",aQute.bnd.service.progress;version="1.0.0",aQute.bnd.service.repository;version="1.2";uses:="aQute.bnd.service,aQute.bnd.version,aQute.service.reporter,org.osgi.resource",aQute.bnd.service.resolve.hook;version="1.0";uses:="org.osgi.resource",aQute.bnd.service.url;version="1.2",aQute.bnd.header;version="1.3.0";uses:="aQute.bnd.version,aQute.service.reporter",aQute.bnd.osgi;version="2.3.0";uses:="aQute.bnd.build,aQute.bnd.header,aQute.bnd.service,aQute.bnd.util.dto,aQute.bnd.version,aQute.service.reporter",aQute.bnd.build;version="2.4.0";uses:="aQute.bnd.maven.support,aQute.bnd.osgi,aQute.bnd.service,aQute.bnd.service.action,aQute.bnd.version,aQute.service.reporter",aQute.bnd.version;version="1.1.0",aQute.bnd.maven.support;version="2.0";uses:="aQute.bnd.service,aQute.bnd.version,aQute.service.reporter,javax.xml.xpath,org.w3c.dom",org.osgi.service.bindex;version="1.0",aQute.service.reporter;version="1.0.1",aQute.bnd.osgi.resource;version="1.4.0";uses:="aQute.bnd.header,aQute.bnd.util.dto,org.osgi.resource",org.osgi.service.repository;version="1.0";uses:="org.osgi.resource",org.osgi.resource;version="1.0",aQute.bnd.util.dto;version="1.0"
+Git-Descriptor 2.4.0.M1-66-gc1ad07d-dirty
+Git-SHA c1ad07dfeb4704ce590bd93c1405d7bfe8bef131
+Import-Package org.apache.tools.ant;resolution:=optional,org.apache.tools.ant.taskdefs;resolution:=optional,org.apache.tools.ant.types;resolution:=optional,aQute.bnd.service;version="[4.1,5)",aQute.bnd.service.action;version="[2.0,2.1)",aQute.bnd.service.diff;version="[1.0,2)",aQute.bnd.service.progress;version="[1.0,2)",aQute.bnd.service.repository;version="[1.2,2)",aQute.bnd.service.url;version="[1.2,2)",aQute.bnd.version;version="[1.1,2)",aQute.service.reporter;version="[1.0,2)",javax.crypto,javax.crypto.spec,javax.naming,javax.net.ssl,javax.script,javax.xml.namespace,javax.xml.parsers,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.stream,javax.xml.xpath,org.osgi.framework;version="[1.6,2)",org.osgi.resource;version="[1.0,2)",org.osgi.service.log;version="[1.3,2)",org.w3c.dom,org.xml.sax,org.xml.sax.helpers,junit.framework;resolution:=optional;version="[3.8,4)"
+Main-Class aQute.bnd.main.bnd
+Manifest-Version 1.0
+Private-Package aQute.bnd.annotation;version="1.43.2",aQute.bnd.annotation.component;version="1.43.1",aQute.bnd.annotation.headers;version="1.0",aQute.bnd.annotation.licenses;version="1.0",aQute.bnd.annotation.metatype;version="1.44.1",aQute.bnd.ant,aQute.bnd.build.model;version="2.6.0",aQute.bnd.build.model.clauses;version=2,aQute.bnd.build.model.conversions,aQute.bnd.compatibility,aQute.bnd.component,aQute.bnd.component.error;version="1.0.0",aQute.bnd.differ;version="1.1.0",aQute.bnd.enroute.commands,aQute.bnd.filerepo;version="1.0",aQute.bnd.gradle,aQute.bnd.help;version="1.1",aQute.bnd.indexer,aQute.bnd.indexer.analyzers,aQute.bnd.main;version="0.9",aQute.bnd.make,aQute.bnd.make.calltree,aQute.bnd.make.component,aQute.bnd.make.coverage,aQute.bnd.make.metatype,aQute.bnd.maven,aQute.bnd.obr,aQute.bnd.osgi.eclipse,aQute.bnd.properties;version="2.0",aQute.bnd.resource.repository,aQute.bnd.signing,aQute.bnd.testing;version="1.0",aQute.bnd.url;version="1.0",aQute.configurable;version="1.0.0",aQute.lib.deployer,embedded-repo.jar,org.osgi.service.component.annotations;version="1.3",org.osgi.service.coordinator;version="1.0",templates,aQute.lib.base64;version="1.2.0",aQute.lib.collections;version="1.2.0",aQute.lib.converter;version="2.0.1",aQute.lib.filter;version="1.1.0",aQute.lib.getopt;version="1.0.0",aQute.lib.hex;version="1.1.0",aQute.lib.io;version="1.4.0",aQute.lib.json;version="3.0.0",aQute.lib.justif;version="1.1.0",aQute.lib.persistentmap;version="1.1.0",aQute.lib.settings;version="1.2.0",aQute.lib.strings;version="1.1.0",aQute.lib.tag;version="1.1",aQute.libg.classdump;version="1.0",aQute.libg.command;version="3.0.0",aQute.libg.cryptography;version="1.1.0",aQute.libg.filelock;version="1.0.0",aQute.libg.filters;version="1.0",aQute.libg.forker;version="1.0",aQute.libg.generics;version="1.0",aQute.libg.glob;version="1.1.1",aQute.libg.map;version="1.2.0",aQute.libg.qtokens;version="1.0",aQute.libg.reporter;version="1.5",aQute.libg.sed;version="1.1.0",aQute.libg.tuple;version="1.0",aQute.lib.markdown
+Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))"
+Tool Bnd-2.4.1.201406261752
+ eclipse [options] ...
+[ -d --dir <string> ] Path to the project[ -w --workspace <string> ] Use the following workspacesync - Synchronized the ./cnf/.settings directory to all the projects Synchronized the ./cnf/.settings directory to all the projects
+ sync
+$ bnd eclipse
+Classpath [/Ws/bnd/biz.aQute.bnd/bin]
+Dependents []
+Sourcepath [/Ws/bnd/biz.aQute.bnd/src, /Ws/bnd/biz.aQute.bnd/test]
+Output /Ws/bnd/biz.aQute.bnd/bin
+ ees <<jar-file>...>
+ export [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -E --exporter <string>* ] [ -o --output <string> ] [ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace exportreport <[list | export | jarexport | readme | jarreadme]> ...
+export - Export the user defined reports. jarexport - Export a custom report of a Jar. jarreadme - Export a readme file of a Jar (template can be parametrized with system properties starting with 'bnd.reporter.*'). list - List the user defined reports. readme - Export a set of readme files (template can be parametrized with system properties starting with 'bnd.reporter.*'). Export the user defined reports.
+ export [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspaceExport a custom report of a Jar.
+ jarexport [options] <jar path> <output path>
+[ -c --configName <string> ] A configuration name defined in the property file (check -reportconfig documentation), if not set a default configuration will be used.[ -l --locale <string> ] A locale (language-COUNTRY-variant) used to localized the report data.[ -p --parameters <string;> ] A list of parameters that will be provided to the transformation process if any. eg: --parameters 'param1=value1,param2=value2'[ -P --properties <string> ] Path to a property file[ -t --template <string> ] Path or URL to a template file used to transform the generated report (twig or xslt). eg: --template bundle.xslt[ -T --templateType <string> ] The template type (aka template file extension), must be set if it could not be guess from the template file name.Export a readme file of a Jar (template can be parametrized with system properties starting with 'bnd.reporter.*').
+ jarreadme [options] <jar path> <output path>
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspaceList the user defined reports.
+ list [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspaceExport the user defined reports.
+ readme [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace extract [options] ...
+[ -c --cdir <string> ] Directory where to store[ -f --file <string> ] Jar file (f option)[ -v --verbose ] Verbose (v option)biz.aQute.bnd (master)$ bnd extract -c generated/tmp generated/biz.aQute.bnd.jar
+ find [options] <[file]...>
+[ -e --exports <glob;> ] Glob expression on the exports.[ -i --imports <glob;> ] Glob expression on the imports.biz.aQute.bnd (master)$ bnd find -e *service* generated/*.jar
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service-4.1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.action-2.0.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.classparser-1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.diff-1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.extension-1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.progress-1.0.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.repository-1.2
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.resolve.hook-1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.bnd.service.url-1.2
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: org.osgi.service.bindex-1.0
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: aQute.service.reporter-1.0.1
+>/Ws/bnd/biz.aQute.bnd/generated/biz.aQute.bnd.jar: org.osgi.service.repository-1.0
+ flatten <input> <output>
+ generate [options] ...
+[ -e --exclude <string;> ] Exclude files by pattern[ -f --force ] Force generation, bypasses file time check[ -p --project <string> ] Identify another project[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace generatemanual <path>
+ gmd ...
+ graph ...
+roots - Find the roots in a set of bundles. A root is a resource that is present but not dependent on by any other resource in the set Find the roots in a set of bundles. A root is a resource that is present but not dependent on by any other resource in the set
+ roots <filespec...>
+ grep [options] <pattern> <file...>
+[ -b --bsn ] Search in bsn[ -e --exports ] Search in exports[ -h --headers <string>* ] Set header(s) to search, can be wildcarded. The default is all headers (*).[ -i --imports ] Search in imports[ -r --resources <string>* ] Search path names of resources. No resources are included unless expressly specified.biz.aQute.bnd (master)$ bnd grep -h "*" "settings" generated/*.jar
+ generated/biz.aQute.bnd.jar : Private-Package ...ute.lib.[settings]...
+ info [options]
+[ -b --buildpath ] [ -c --classpath ] [ -d --dependsOn ] [ -p --project <string> ] [ -r --runbundles ] [ -s --sourcepath ] [ -v --vmpath ] biz.aQute.bnd (master)$ bnd info -b
+found password
+Build [/Ws/bnd/biz.aQute.bnd/bin, /Ws/bnd/aQute.libg/generated/aQute.libg.jar, /Ws/bnd/biz.aQute.bndlib/bin, /Ws/bnd/cnf/repo/org.apache.tools.ant/org.apache.tools.ant-1.6.5.jar, /Ws/bnd/cnf/repo/org.osgi.service.component.annotations/org.osgi.service.component.annotations-6.0.0.jar, /Ws/bnd/cnf/repo/osgi.cmpn/osgi.cmpn-4.3.1.jar, /Ws/bnd/cnf/repo/osgi.core/osgi.core-4.3.1.jar, /Ws/bnd/cnf/repo/org.osgi.impl.bundle.bindex/org.osgi.impl.bundle.bindex-2.2.0.jar, /Ws/bnd/cnf/repo/osgi.r5/osgi.r5-1.0.1.jar]
+
+Class path []
+
+Depends on [aQute.libg, biz.aQute.bndlib, biz.aQute.junit, biz.aQute.launcher]
+
+Run []
+
+Run path [/Ws/bnd/cnf/repo/org.eclipse.osgi/org.eclipse.osgi-3.6.0.jar, /Ws/bnd/cnf/repo/com.springsource.junit/com.springsource.junit-3.8.2.jar]
+
+Source [/Ws/bnd/biz.aQute.bnd/src]
+
+biz.aQute.bnd (master)$ bnd info -bcdrsv
+found password
+Build [/Ws/bnd/biz.aQute.bnd/bin, /Ws/bnd/aQute.libg/generated/aQute.libg.jar, /Ws/bnd/biz.aQute.bndlib/bin, /Ws/bnd/cnf/repo/org.apache.tools.ant/org.apache.tools.ant-1.6.5.jar, /Ws/bnd/cnf/repo/org.osgi.service.component.annotations/org.osgi.service.component.annotations-6.0.0.jar, /Ws/bnd/cnf/repo/osgi.cmpn/osgi.cmpn-4.3.1.jar, /Ws/bnd/cnf/repo/osgi.core/osgi.core-4.3.1.jar, /Ws/bnd/cnf/repo/org.osgi.impl.bundle.bindex/org.osgi.impl.bundle.bindex-2.2.0.jar, /Ws/bnd/cnf/repo/osgi.r5/osgi.r5-1.0.1.jar]
+
+Class path []
+
+Depends on [aQute.libg, biz.aQute.bndlib, biz.aQute.junit, biz.aQute.launcher]
+
+Run []
+
+Run path [/Ws/bnd/cnf/repo/org.eclipse.osgi/org.eclipse.osgi-3.6.0.jar, /Ws/bnd/cnf/repo/com.springsource.junit/com.springsource.junit-3.8.2.jar]
+
+Source [/Ws/bnd/biz.aQute.bnd/src]
+ junit [options] <testclass[:method]...>
+[ -c --continuous ] Set the -testcontinuous flag[ -e --exclude <string;> ] Exclude files by pattern[ -f --force ] Launch the test even if this bundle does not contain Test-Cases[ -p --project <string> ] Identify another project[ -t --trace ] Set the -runtrace flag[ -v --verbose ] prints more processing information[ -V --verify ] Verify all the dependencies before launching (runpath, runbundles, testpath)[ -w --workspace <string> ] Use the following workspace macro [options] <<macro>> <[...]>
+[ -p --project <string> ] Path to project, default current directorybiz.aQute.bnd (master)$ bnd macro p
+biz.aQute.bnd
+biz.aQute.bnd (master)$
+ maven ...
+maven
+[-temp <dir>] use as temp directory
+settings show maven settings
+bundle turn a bundle into a maven bundle
+ [-properties <file>] provide properties, properties starting with javadoc are options for javadoc, like javadoc-tag=...
+ [-javadoc <file|url>] where to find the javadoc (zip/dir), otherwise generated
+ [-source <file|url>] where to find the source (zip/dir), otherwise from OSGI-OPT/src
+ [-scm <url>] required scm in pom, otherwise from Bundle-SCM
+ [-url <url>] required project url in pom
+ [-bsn bsn] overrides bsn
+ [-version <version>] overrides version
+ [-developer <email>] developer email
+ [-nodelete] do not delete temp files
+ [-passphrase <gpgp passphrase>] signer password
+ <file|url>
+biz.aQute.bnd (master)$ bnd maven settings
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xmlns="http://maven.apache.org/settings/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
+ <!-- <localRepository>C:/Users/franklan/.m2/repository</localRepository> -->
+ <pluginGroups>
+ <!-- pluginGroup
+ | Specifies a further group identifier to use for plugin lookup.
+ <pluginGroup>com.your.plugins</pluginGroup>
+ -->
+ </pluginGroups>
+ <servers>
+ <server>
+ <id>deploymentRepo</id>
+ <username>deployment</username>
+ <password>deployment123</password>
+ <!-- <privateKey>c://config//Frank_Langel.p12</privateKey> -->
+ </server>
+ </servers>
+ <mirrors>
+ <mirror>
+ <id>nexus</id>
+ <mirrorOf>*</mirrorOf>
+ <url>https://svn.myfarm365.de/nexus/content/groups/public</url>
+ <privateKey>/Users/aqute/Desktop/Peter_Kriens.p12</privateKey>
+ </mirror>
+ </mirrors>
+ <profiles>
+ <profile>
+ <id>nexus</id>
+ <!--Enable snapshots for the built in central repo to direct -->
+ <!--all requests to nexus via the mirror -->
+ <repositories>
+ <repository>
+ <id>central</id>
+ <url>http://central</url> <!-- URL wird nicht verwendet, da mirror alles zu nexus weiterleitet -->
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>always</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ <updatePolicy>always</updatePolicy>
+ </snapshots>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <url>http://central</url> <!-- URL wird nicht verwendet, da mirror alles zu nexus weiterleitet -->
+ <id>central</id>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>always</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ <updatePolicy>always</updatePolicy>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+
+ </profiles>
+ <activeProfiles>
+ <activeProfile>nexus</activeProfile>
+ </activeProfiles>
+</settings>
+ mbr [options] ...
+[ -j --json ] Output to json instead of human readable when possiblecheck - For each archive in the index, show the available higher versions repos - List the repositories in this workspace update - For each archive in the index, update to a higher version if available in the repository verify - Verify the repositories, this checks if a GAV is defined in multiple repositories or if there are multiple revisions for the same program For each archive in the index, show the available higher versions
+ check [options] <archive-glob...>
+[ -r --repo <[i> ] Select the repositories by index (see list for getting the index)[ -s --scope <scope> ] Specify the scope of the selected version: all, micro (max), minor (max), major (max)[ -S --snapshotlike ] Include snapshot like versions like -SNAPSHOT, -rc1, -beta12. These are skipped for updated by defaultList the repositories in this workspace
+ repos
+For each archive in the index, update to a higher version if available in the repository
+ update [options] <archive-glob...>
+[ -d --dry ] [ -r --repo <[i> ] Select the repositories by index (see list for getting the index)[ -s --scope <scope> ] Specify the scope of the selected version: all, micro (max), minor (max), major (max)[ -S --snapshotlike ] Include snapshot like versions like -SNAPSHOT, -rc1, -beta12. These are skipped for updated by defaultVerify the repositories, this checks if a GAV is defined in multiple repositories or if there are multiple revisions for the same program
+ verify [options] <archive-glob...>
+[ -r --repo <[i> ] Select the repositories by index (see list for getting the index) nexus [options] <sub-cmd> ...
+[ -c --compatible <compatible> ] [ -s --settings <string> ] Specify the connection-settings for the HttpClient. Default looks for the normal settings files.[ -u --url <uri> ] Specify the URL of the Nexus repository.createstaging - Create a staging repository. The profileId specifies a particular profile. If you go to nexus, select the staging profiles, and then select the profile you want to use. The profile id is then in the url. delete - Delete a file in a staging repository by id fetch - Fetch a file to staging repository by id files - index - sign - upload - Upload a file to staging repository by id Create a staging repo
+ createstaging [options] <profileId>
+[ -d --description <string> ] delete [options] <repositoryId> <remotepath_or_gav>
+[ -f --force ] fetch [options] <repositoryId> <remotepath_or_gav>
+[ -f --force ] [ -o --output <file> ] files [options] <files...>
+[ -e --exclude <string> ] A resource URI is only include if the include pattern appears in the path and the exclude does not appear[ -i --include <string> ] A resource URI is only include if the include pattern appears in the path and the exclude does not appear[ -r --relative ] index [options] ...
+[ -d --depth <int> ] [ -n --name <string> ] [ -o --output <string> ] [ -r --referal <uri> ] Artifact signing subcommand.
+ sign [options] <path...>
+[ -c --command <string> ] Specify the path to the gpg command. The gpg path can also be specified using the 'gpg' system property or the 'GPG' environment variable. Defaults to 'gpg'.[ -f --from <uri> ] Specify the URL to a Nexus repository from which to obtain the artifacts to sign. Defaults to signing the specified paths to the sign subcommand.[ -i --include <string> ] Specify the include pattern for artifacts from the '--from' option. Defaults to '**'.[ -k --key <string> ] Specify the local-user USER-ID for signing. Defaults to signing with the default key.[ -p --password <string> ] Specify the passpharse to the gpg command. Defaults to reading stdin for the passphrase.[ -s --show ] Only compute and display the signatures but do not upload them.[ -t --threads <int> ] Specify the number of threads to use when downloading, signing, and uploading artifacts. Defaults to one thread.[ -x --xclude <string> ] Specify the exclude pattern for artifacts from the '--from' option. Defaults to no exclude pattern. upload [options] <repositoryId> <remotepath_or_gav> <file>
+[ -f --force ] package [options] <<bnd|bndrun>> <[...]>
+[ -o --output <string> ] Where to store the resulting file. Default the name of the bnd file with a .jar extension.[ -p --profile <string> ] Profile name. Default no profile packagetoresource ...
+ plugins [options] ...
+[ -p --project <string> ] Identify another projectbiz.aQute.bnd (master)$ bnd plugins
+found password
+000 aQute.bnd.build.Workspace
+001 java.util.concurrent.ThreadPoolExecutor@2685c106[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
+002 java.util.Random@174384ac
+003 aQute.bnd.maven.support.Maven@51bb4422
+004 aQute.lib.settings.Settings@5d763e19
+005 bnd-cache
+006 aQute.bnd.resource.repository.ResourceRepositoryImpl@67528259
+007 aQute.bnd.osgi.Processor
+008 /Ws/bnd/cnf/repo r/w=true
+009 /Ws/bnd/dist/bundles r/w=true
+010 aQute.bnd.signing.JartoolSigner@2dac2cb7
+ print [options] <jar-file...>
+[ -a --api ] Print the api usage. This shows the usage constraints on exported packages when only public API is used.[ -b --by ] Transposed uses. Will show for each known package who it is used by.[ -c --capabilities ] Show the capabilities[ -C --component ] Show components in detail[ -f --full ] Print all except list[ -i --impexp ] List the imports exports, versions and ranges[ -j --java ] Keep references to java in --api, --uses, and --usedby.[ -l --list ] List the resources[ -m --manifest ] Print the manifest.[ -t --typemeta ] Show any metatype data[ -u --uses ] Show for each contained package, what other package it uses. Is either an private, exported, or imported package[ -v --verify ] Before printing, verify that the bundle is correct.[ -x --xport ] Show all packages, not just exported, in the API viewbiz.aQute.bnd (master)$ bnd print generated/biz.aQute.bnd.jar
+[MANIFEST biz.aQute.bnd]
+Bnd-LastModified 1404918434023
+Bundle-Copyright Copyright (c) aQute (2000, 2014). All Rights Reserved.
+Bundle-Description This command line utility is the Swiss army knife of OSGi. It provides you with a breadth
+Bundle-DocURL http://www.aQute.biz/Code/Bnd
+Bundle-License http://www.opensource.org/licenses/apache2.0.php; description="Apache License, Version 2.0"; link=http://www.apache.org/licenses/LICENSE-2.0.html
+Bundle-ManifestVersion 2
+Bundle-Name biz.aQute.bnd
+Bundle-SCM git://github.com/bndtools/bnd.git
+Bundle-SymbolicName biz.aQute.bnd
+Bundle-Vendor aQute SARL http://www.aQute.biz
+Bundle-Version 2.4.0.201407091507
+Conditional-Package aQute.libg.*,aQute.lib.*,aQute.configurable
+Created-By 1.8.0 (Oracle Corporation)
+Export-Package aQute.bnd.service;version="4.1.0";uses:="aQute.bnd.build,aQute.bnd.osgi,aQute.bnd.version,aQute.service.reporter",aQute.bnd.service.action;version="2.0.0";uses:="aQute.bnd.build",aQute.bnd.service.classparser;version="1.0";uses:="aQute.bnd.osgi",aQute.bnd.service.diff;version="1.0";uses:="aQute.bnd.osgi",aQute.bnd.service.extension;version="1.0";uses:="aQute.bnd.build",aQute.bnd.service.progress;version="1.0.0",aQute.bnd.service.repository;version="1.2";uses:="aQute.bnd.service,aQute.bnd.version,aQute.service.reporter,org.osgi.resource",aQute.bnd.service.resolve.hook;version="1.0";uses:="org.osgi.resource",aQute.bnd.service.url;version="1.2",aQute.bnd.header;version="1.3.0";uses:="aQute.bnd.version,aQute.service.reporter",aQute.bnd.osgi;version="2.3.0";uses:="aQute.bnd.build,aQute.bnd.header,aQute.bnd.service,aQute.bnd.util.dto,aQute.bnd.version,aQute.service.reporter",aQute.bnd.build;version="2.4.0";uses:="aQute.bnd.maven.support,aQute.bnd.osgi,aQute.bnd.service,aQute.bnd.service.action,aQute.bnd.version,aQute.service.reporter",aQute.bnd.version;version="1.1.0",aQute.bnd.maven.support;version="2.0";uses:="aQute.bnd.service,aQute.bnd.version,aQute.service.reporter,javax.xml.xpath,org.w3c.dom",org.osgi.service.bindex;version="1.0",aQute.service.reporter;version="1.0.1",aQute.bnd.osgi.resource;version="1.4.0";uses:="aQute.bnd.header,aQute.bnd.util.dto,org.osgi.resource",org.osgi.service.repository;version="1.0";uses:="org.osgi.resource",org.osgi.resource;version="1.0",aQute.bnd.util.dto;version="1.0"
+Git-Descriptor 2.4.0.M1-66-gc1ad07d-dirty
+Git-SHA c1ad07dfeb4704ce590bd93c1405d7bfe8bef131
+Import-Package org.apache.tools.ant;resolution:=optional,org.apache.tools.ant.taskdefs;resolution:=optional,org.apache.tools.ant.types;resolution:=optional,aQute.bnd.service;version="[4.1,5)",aQute.bnd.service.action;version="[2.0,2.1)",aQute.bnd.service.diff;version="[1.0,2)",aQute.bnd.service.progress;version="[1.0,2)",aQute.bnd.service.repository;version="[1.2,2)",aQute.bnd.service.url;version="[1.2,2)",aQute.bnd.version;version="[1.1,2)",aQute.service.reporter;version="[1.0,2)",javax.crypto,javax.crypto.spec,javax.naming,javax.net.ssl,javax.script,javax.xml.namespace,javax.xml.parsers,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.stream,javax.xml.xpath,org.osgi.framework;version="[1.6,2)",org.osgi.resource;version="[1.0,2)",org.osgi.service.log;version="[1.3,2)",org.w3c.dom,org.xml.sax,org.xml.sax.helpers,junit.framework;resolution:=optional;version="[3.8,4)"
+Main-Class aQute.bnd.main.bnd
+Manifest-Version 1.0
+Private-Package aQute.bnd.annotation;version="1.43.2",aQute.bnd.annotation.component;version="1.43.1",aQute.bnd.annotation.headers;version="1.0",aQute.bnd.annotation.licenses;version="1.0",aQute.bnd.annotation.metatype;version="1.44.1",aQute.bnd.ant,aQute.bnd.build.model;version="2.6.0",aQute.bnd.build.model.clauses;version=2,aQute.bnd.build.model.conversions,aQute.bnd.compatibility,aQute.bnd.component,aQute.bnd.component.error;version="1.0.0",aQute.bnd.differ;version="1.1.0",aQute.bnd.enroute.commands,aQute.bnd.filerepo;version="1.0",aQute.bnd.gradle,aQute.bnd.help;version="1.1",aQute.bnd.indexer,aQute.bnd.indexer.analyzers,aQute.bnd.main;version="0.9",aQute.bnd.make,aQute.bnd.make.calltree,aQute.bnd.make.component,aQute.bnd.make.coverage,aQute.bnd.make.metatype,aQute.bnd.maven,aQute.bnd.obr,aQute.bnd.osgi.eclipse,aQute.bnd.properties;version="2.0",aQute.bnd.resource.repository,aQute.bnd.signing,aQute.bnd.testing;version="1.0",aQute.bnd.url;version="1.0",aQute.configurable;version="1.0.0",aQute.lib.deployer,embedded-repo.jar,org.osgi.service.component.annotations;version="1.3",org.osgi.service.coordinator;version="1.0",templates,aQute.lib.base64;version="1.2.0",aQute.lib.collections;version="1.2.0",aQute.lib.converter;version="2.0.1",aQute.lib.filter;version="1.1.0",aQute.lib.getopt;version="1.0.0",aQute.lib.hex;version="1.1.0",aQute.lib.io;version="1.4.0",aQute.lib.json;version="3.0.0",aQute.lib.justif;version="1.1.0",aQute.lib.persistentmap;version="1.1.0",aQute.lib.settings;version="1.2.0",aQute.lib.strings;version="1.1.0",aQute.lib.tag;version="1.1",aQute.libg.classdump;version="1.0",aQute.libg.command;version="3.0.0",aQute.libg.cryptography;version="1.1.0",aQute.libg.filelock;version="1.0.0",aQute.libg.filters;version="1.0",aQute.libg.forker;version="1.0",aQute.libg.generics;version="1.0",aQute.libg.glob;version="1.1.1",aQute.libg.map;version="1.2.0",aQute.libg.qtokens;version="1.0",aQute.libg.reporter;version="1.5",aQute.libg.sed;version="1.1.0",aQute.libg.tuple;version="1.0",aQute.lib.markdown
+Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))"
+Tool Bnd-2.4.1.201406261752
+
+[IMPEXP]
+Import-Package
+ aQute.bnd.service {version=[4.1,5)}
+ aQute.bnd.service.action {version=[2.0,2.1)}
+ aQute.bnd.service.diff {version=[1.0,2)}
+ aQute.bnd.service.progress {version=[1.0,2)}
+ aQute.bnd.service.repository {version=[1.2,2)}
+ aQute.bnd.service.url {version=[1.2,2)}
+ aQute.bnd.version {version=[1.1,2)}
+ aQute.service.reporter {version=[1.0,2)}
+ javax.crypto
+ javax.crypto.spec
+ javax.naming
+ javax.net.ssl
+ javax.script
+ javax.xml.namespace
+ javax.xml.parsers
+ javax.xml.transform
+ javax.xml.transform.dom
+ javax.xml.transform.stream
+ javax.xml.xpath
+ junit.framework {resolution:=optional, version=[3.8,4)}
+ org.apache.tools.ant {resolution:=optional}
+ org.apache.tools.ant.taskdefs {resolution:=optional}
+ org.apache.tools.ant.types {resolution:=optional}
+ org.osgi.framework {version=[1.6,2)}
+ org.osgi.resource {version=[1.0,2)}
+ org.osgi.service.log {version=[1.3,2)}
+ org.w3c.dom
+ org.xml.sax
+ org.xml.sax.helpers
+Export-Package
+ aQute.bnd.build {version=2.4.0}
+ aQute.bnd.header {version=1.3.0}
+ aQute.bnd.maven.support {version=2.0}
+ aQute.bnd.osgi {version=2.3.0}
+ aQute.bnd.osgi.resource {version=1.4.0}
+ aQute.bnd.service {version=4.1.0, imported-as=[4.1,5)}
+ aQute.bnd.service.action {version=2.0.0, imported-as=[2.0,2.1)}
+ aQute.bnd.service.classparser {version=1.0}
+ aQute.bnd.service.diff {version=1.0, imported-as=[1.0,2)}
+ aQute.bnd.service.extension {version=1.0}
+ aQute.bnd.service.progress {version=1.0.0, imported-as=[1.0,2)}
+ aQute.bnd.service.repository {version=1.2, imported-as=[1.2,2)}
+ aQute.bnd.service.resolve.hook {version=1.0}
+ aQute.bnd.service.url {version=1.2, imported-as=[1.2,2)}
+ aQute.bnd.util.dto {version=1.0}
+ aQute.bnd.version {version=1.1.0, imported-as=[1.1,2)}
+ aQute.service.reporter {version=1.0.1, imported-as=[1.0,2)}
+ org.osgi.resource {version=1.0, imported-as=[1.0,2)}
+ org.osgi.service.bindex {version=1.0}
+ org.osgi.service.repository {version=1.0}
+ profile <create> ...
+create - create [options] ...
+[ -b --bsn <string> ] [ -e --extension <glob> ] [ -m --match <instructions> ] [ -o --output <string> ] [ -p --properties <string;> ] [ -v --version <version> ] project [options] ...
+[ -p --project <string> ] Identify another projectbiz.aQute.bnd (master)$ bnd project
+Name biz.aQute.bnd
+Actions [build, test, run, clean, release, refresh, deploy]
+Directory /Ws/bnd/biz.aQute.bnd
+Depends on [aQute.libg, biz.aQute.bndlib, biz.aQute.junit, biz.aQute.launcher]
+Sub builders [biz.aQute.bnd]
+ properties [options]
+[ -k --key <glob> ] Filter on key[ -l --local ] Get the inherited properties[ -p --project <string> ] Identify another project[ -v --value <glob> ] Filter on value release [options] ...
+[ -p --project <string> ] Path to project, default is current project[ -r --repo <string> ] Set the release repository[ -t --test ] Release with test build[ -w --workspace ] Release all bundles in in the workspace remote [options] ...
+[ -h --host <string> ] Specify the host to commicate with, default is 'localhost'[ -p --port <int> ] Specify the port to commicate with, default is 29998distro - framework - install - Install/update the specified bundle. list - List the bundles installed in the remote framework ping - revisions - start - Start the specified bundles stop - Stop the specified bundles uninstall - Uninstall the specified bundles Create a distro jar (or xml) from a remote agent
+ distro [options] <bsn> <[version]>
+[ -c --copyright <string> ] The Bundle-Copyright header[ -d --description <string> ] The Bundle-Description header[ -l --license <string> ] The Bundle-License header[ -o --output <string> ] Output name[ -v --vendor <string> ] The Bundle-Vendor header[ -x --xml ] Generate xml instead of a jar with manifestGet the framework info
+ framework
+Communicate with the remote framework to install or update bundle
+ install [options] <filespec...>
+[ -l --location <string;> ] By default the location is 'manual:Communicate with the remote framework to list the installed bundles
+ list [options] ...
+[ -j --json ] Specify to return the output as JSONPing the remote framework
+ ping
+Get the bundle revisions
+ revisions <bundleid...>
+Communicate with the remote framework to perform bundle operation
+ start <bundleId...>
+Communicate with the remote framework to perform bundle operation
+ stop <bundleId...>
+Communicate with the remote framework to perform bundle operation
+ uninstall <bundleId...>
+ remove <what> <[name]...>
+plugin - Remove a plugin from the workspace project - Remove a project from workspace workspace - Remove workspace Remove a plugin from the workspace
+ plugin <alias...>
+Remove a project from workspace
+ project <name> ...
+Remove workspace
+ workspace <name> ...
+ repo [options] <sub-cmd> ...
+[ -c --cache ] Include the cache repository[ -f --filerepo <string>* ] Add a File Repository[ -m --maven ] Include the maven repository[ -p --project <string> ] Specify a project[ -r --release <glob> ] Override the name of the release repository (-releaserepo)[ -w --workspace <string> ] Workspace (a standalone bndrun file or a sbdirectory of a workspace (default is the cwd)copy - diff - Diff jars (or show tree) get - Get an artifact from a repository. index - list - List all artifacts from the current repositories with their versions put - Put an artifact into the repository after it has been verified. refresh - Refresh refreshable repositories repos - List the current repositories sync - topom - versions - Displays a list of versions for a given bsn that can be found in the current repositories. copy [options] <source> <dest> <bsn[:version]...>
+[ -d --dry ] Do not really copy but trace the steps[ -f --filter <string;> ] [ -F --force ] [ -p --project <string> ] Identify another project[ -q --quiet ] [ -s --standalone <string> ] A stanalone bndrun fileShow the diff tree of a single repo or compare 2 repos. A diff tree is a detailed tree of all aspects of a bundle, including its packages, types, methods, fields, and modifiers.
+ diff [options] <newer repo> <[older repo]>
+[ -a --added ] Just additions (no removes)[ -A --all ] Both add and removes[ -d --diff ] Formatted like diff[ -f --full ] Show full diff tree (also wen entries are equal)[ -j --json ] Serialize to JSON[ -r --remove ] Just removes (no additions)Get an artifact from a repository.
+ get [options] <bsn> <[range]>
+[ -f --from <instruction> ] [ -l --lowest ] [ -o --output <string> ] Where to store the artifact index [options] ...
+[ -f --from <instruction> ] A glob expression on the source repo, default is all repos[ -n --name <string> ] The name of the output file. If not set will show on the console[ -o --output <string> ] Output file (will be compressed)[ -q --query <string> ] Optional search term for the list of bsns (given to the repo)[ -Q --quiet ] No outputList all artifacts from the current repositories with their versions
+ list [options]
+[ -f --from <instruction> ] A glob expression on the source repo, default is all repos[ -n --noversions ] Do not list the versions, just the bsns[ -q --query <string> ] Optional search term for the list of bsns (given to the repo)Put an artifact into the repository after it has been verified.
+ put [options] <<jar>...>
+[ -f --force ] Put in repository even if verification fails (actually, no verification is done).Refresh refreshable repositories
+ refresh [options]
+[ -q --quiet ] List the current repositories
+ repos
+ sync [options] ...
+[ -d --dest <string> ] [ -g --gavs <string;> ] [ -s --source <string;> ] [ -w --workspace <string> ] Create a POM out of a bnd repository
+ topom [options] <repo> <name>
+[ -d --dependencyManagement ] Use the dependency management section[ -o --output <string> ] Output file[ -p --parent <string> ] The parent of the pom (default none.xml)Displays a sorted set of versions for a given bsn that can be found in the current repositories.
+ versions <bsn>
+ resolve [options] <<path>...>
+[ -b --bundles ] Print out the bundles[ -d --dot ] Create a dependency file[ -e --exclude <string;> ] Exclude files by pattern[ -f --files ] Print out the bundle files[ -o --optionals ] Show the optionals[ -p --project <string> ] Identify another project[ -q --quiet ] Quiet[ -r --runorder <runorder> ] Override the -runorder[ -u --urls ] Print out the bundle urls[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace[ -W --write ] Write -runbundles instruction back to the file[ -x --xchange ] Fail on changesdot - Create a dot file repos - resolve - Resolve a bndrun file validate - Resolve a repository index against a base to determine if the index is 'complete' Create a dot file
+ dot [options] <bndrun-file>
+[ -o --output <string> ] Send to file[ -q --quiet ] Quiet[ -r --runorder <runorder> ] Override the -runorder repos [options] ...
+[ -w --workspace <string> ] Resolve a bndrun file
+ resolve [options] <<path>...>
+[ -b --bundles ] Print out the bundles[ -d --dot ] Create a dependency file[ -e --exclude <string;> ] Exclude files by pattern[ -f --files ] Print out the bundle files[ -o --optionals ] Show the optionals[ -p --project <string> ] Identify another project[ -q --quiet ] Quiet[ -r --runorder <runorder> ] Override the -runorder[ -u --urls ] Print out the bundle urls[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspace[ -W --write ] Write -runbundles instruction back to the file[ -x --xchange ] Fail on changesValidate an OBR file by trying to resolve each entry against itself
+ validate [options] <[index-path]>
+[ -a --all ] Include all output details[ -c --capabilities <parameters> ] Specify a set of capabilities provided by the base[ -C --core <osgi_core> ] Specify the framework version used as part of the base, [R4_0_1 R4_2_1 R4_3_0 R4_3_1 R5_0_0 R6_0_0 R7_0_0 R8_0_0][ -e --ee <ee> ] Specify the execution environment used as part of the base, default is JavaSE_1_8[ -f --failedshow ] Show resolution failed errors[ -p --packages <parameters> ] Specify a set of packages provided by the base[ -s --system <string> ] Specify a system file used as the base (more commonly referred to as a 'distro')[ -u --unused ] Show any unused entries. This will only try to resolve the workspace entries and then list the entries in the repos that are not used[ -x --xref ] List cross reference: mimssing-req -> resources* run [options] <[bndrun]>
+[ -p --project <string> ] Path to another project than the current project. Only valid if no bndrun is specified[ -v --verify ] Verify all the dependencies before launching (runpath, runbundles) runtests [options] ...
+[ -d --dir <string> ] Path to work directory[ -e --exclude <string;> ] Exclude files by pattern[ -r --reportdir <string> ] Report directory[ -t --tests <string;> ] Test names to execute[ -T --title <string> ] Title in the report[ -v --verbose ] prints more processing information[ -w --workspace <string> ] Use the following workspacebnd runtests --tests org.osgi.test.cases.tracker.junit.BundleTrackerTests:testSubclass,org.osgi.test.cases.tracker.junit.BundleTrackerTests:testModified org.osgi.test.cases.tracker.bnd
+ schema [options] ...
+[ -o --output <string> ] Output file[ -x --xsl <string> ] Specify an XSL file for pretty printing select [options] <<jar-path>> <[...]>
+[ -h --header <string>* ] A manifest header to print or: path, name, size, length, modified for information about the file, wildcards are allowed to print multiple headers. [ -k --key ] Print the key before the value[ -n --name ] Print the file name before the value[ -p --path ] Print the file path before the value[ -w --where <string> ] A simple assertion on a manifest header (e.g. Bundle-Version=1.0.1) or an OSGi filter that is asserted on all manifest headers. Comparisons are case insensitive. The key 'resources' holds the pathnames of all resources and can also be asserted to check for the presence of a header.biz.aQute.bnd (master)$ bnd select -h name generated/*.jar
+biz.aQute.bnd.jar
+biz.aQute.bnd (master)$ bnd select -h size generated/*.jar
+2604654
+ settings [options] <<key>[=<value>]...>
+[ -b --base64 ] Show key in base64[ -c --clear ] Clear all the settings, including the public and private key[ -g --generate ] Generate a new private/public key pair[ -l --location <string> ] Override the default "~/.bnd/settings.json" location[ -m --mac ] Sign the strings on the commandline[ -p --password <[c> ] Password for local file[ -P --publicKey ] Show the public key[ -s --secretKey ] Show the private secret key shell [options] ...
+[ -p --project <string> ] Identify another projectThe shell function in bnd is primarily intended to exercise macros. Although the macro command made it possible to test a single macro, the awful interaction between the (ba)sh character interpretations for $ and quotes made this quite hard to use in practice. the shell therefore directly talks the macro language as you write it in a bnd.bnd file. Additionally, all bnd commands are also available.
$ bnd shell
+Base Project com.example.project
+> p
+com.example.project
+> now
+Fri Sep 28 11:29:02 CEST 2018
+>
+When you start the shell bnd will try to find a project. If the -p options is specified it will first look in that directory, otherwise it will look in the current working directory. If no project is found, it will try to find the workspace set by bnd. If no workspace can be found, bnd will use the bnd defaults as properties.
A project inherits all properties from the workspace. So when bnd has a project in scope then all macros and properties are available defined in the project's bnd.bnd file, the ./cnf/build.bnd file, and any files in ./cnf/ext/*.bnd. For example, javac.source is a property set by the workspace:
> javac.source
+1.8
+>
+This raises the question: What properties are there? The shell also supports bnd commands and there is a properties command. However, there are a large number of properties so lets limit it to the properties that start with java:
> properties -k java*
+javac.compliance 1.8
+javac.source 1.8
+javac.target 1.8
+>
+ source [options] <<jar path>> <<source path>>
+[ -o --output <string> ] The output file sync [options] ...
+[ -p --project <string> ] Identify another project syntax [options] <header|instruction> ...
+[ -w --width <int> ] The width of the printoutbiz.aQute.bnd (master)$ bnd syntax Bundle-Version
+
+[Bundle-Version]
+ The Bundle-Version header specifies the version of this bundle.
+
+ Pattern : [0-9]{1,9}(\.[0-9]{1,9}(\.[0-9]{1,9}(\.[0-9A-Za-z_-]+)?)?)?
+ Example : Bundle-Version: 1.23.4.build200903221000
+ test [options] <testclass[:method]...>
+[ -c --continuous ] Set the -testcontinuous flag[ -e --exclude <string;> ] Exclude files by pattern[ -f --force ] Launch the test even if this bundle does not contain Test-Cases[ -p --project <string> ] Identify another project[ -t --trace ] Set the -runtrace flag[ -v --verbose ] prints more processing information[ -V --verify ] Verify all the dependencies before launching (runpath, runbundles, testpath)[ -w --workspace <string> ] Use the following workspace type [options] ...
+[ -f --file <string> ] Jar file (f option)[ -v --verbose ] Verbose (v option) verify <<jar path>> <[...]>
+ version [options]
+[ -x --xtra ] Show licensing, copyright, sha, scm, etcbiz.aQute.bnd (master)$ bnd version -x
+Version 2.4.0.201407091507
+From Wed Jul 09 17:07:14 CEST 2014
+License Apache License, Version 2.0
+Copyright Copyright (c) aQute (2000, 2014). All Rights Reserved.
+Git-SHA c1ad07dfeb4704ce590bd93c1405d7bfe8bef131
+Git-Descriptor 2.4.0.M1-66-gc1ad07d-dirty
+Sources git://github.com/bndtools/bnd.git
+biz.aQute.bnd (master)$
+ view [options] <<jar-file>> <<resource>> <[...]>
+[ -c --charset <string> ] Character set to use for viewing wrap [options] <<jar-file>> <[...]>
+[ -b --bsn <string> ] Set the bundle symbolic name to use[ -c --classpath <string>* ] A classpath specification[ -f --force ] Allow override of an existing file[ -o --output <string> ] Path to the output, default the name of the input jar with the '.bar' extension. If this is a directory, the output is place there.[ -p --properties <string> ] A file with properties in bnd format.[ -v --version <version> ] Set the version to useThe wrap command takes an existing JAR file and guesses the manifest headers that will make this JAR useful for an OSGi Service Platform. If the output file is not overridden, the name of the input file is used with a .bar extension. The default bnd file for the header calculation is:
+Export-Package: *
+Import-Package: <packages inside the target jar>
+If the target bundle has a manifest, the headers are merged with the properties.
+The defaults can be overridden with a specific properties file.
+bnd wrap -classpath osgi.jar *.jar
xmlrepodiff [options] <newer XML resource repository> <older XML resource repository>
+[ -e --expandfilter ] Expand 'filter' directives[ -i --ignore <string> ] Ignore elements from the comparison result (Format: type=name,..) e.g. RESOURCE_ID=org.apache.felix.scr#com.company.runtime.,CAPABILITY=bnd.workspace.project#osgi.wiring.package:javax.xml.,ATTRIBUTE=bundle-symbolic-name:system.bundle,REQUIREMENT=osgi.wiring.package:org.xml.*[ -s --showall ] Display all (changed and unchanged both)MAJOR REPOSITORY <repository>
+ MAJOR RESOURCE_ID org.apache.felix.configadmin
+ MAJOR CAPABILITIES <capabilities>
+ MAJOR CAPABILITY bnd.maven:org.apache.felix:org.apache.felix.configadmin
+ REMOVED ATTRIBUTE maven-version:1.9.10
+ ADDED ATTRIBUTE maven-version:1.9.22
+ REMOVED CAPABILITY osgi.content:77B03B938E796C0512D9AD89ACF287CCE09C14A159CA05B6CB74DDA17E7AB3FA
+ REMOVED ATTRIBUTE mime:application/vnd.osgi.bundle
+ REMOVED ATTRIBUTE size:155630
+ REMOVED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/apache/felix/org.apache.felix.configadmin/1.9.10/org.apache.felix.configadmin-1.9.10.jar
+ ADDED CAPABILITY osgi.content:B349E16D60DA66B6DA70AB3E056677A9D6A0B8953DF84ECD63B10AA5EF3C5865
+ ADDED ATTRIBUTE mime:application/vnd.osgi.bundle
+ ADDED ATTRIBUTE size:168301
+ ADDED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/apache/felix/org.apache.felix.configadmin/1.9.22/org.apache.felix.configadmin-1.9.22.jar
+ MAJOR CAPABILITY osgi.identity:org.apache.felix.configadmin
+ REMOVED ATTRIBUTE version:1.9.10
+ ADDED ATTRIBUTE version:1.9.22
+ MAJOR CAPABILITY osgi.wiring.bundle:org.apache.felix.configadmin
+ REMOVED ATTRIBUTE bundle-version:1.9.10
+ ADDED ATTRIBUTE bundle-version:1.9.22
+ MAJOR CAPABILITY osgi.wiring.host:org.apache.felix.configadmin
+ REMOVED ATTRIBUTE bundle-version:1.9.10
+ ADDED ATTRIBUTE bundle-version:1.9.22
+ MAJOR CAPABILITY osgi.wiring.package:org.apache.felix.cm
+ REMOVED ATTRIBUTE bundle-version:1.9.10
+ ADDED ATTRIBUTE bundle-version:1.9.22
+ MAJOR CAPABILITY osgi.wiring.package:org.apache.felix.cm.file
+ REMOVED ATTRIBUTE bundle-version:1.9.10
+ ADDED ATTRIBUTE bundle-version:1.9.22
+ MAJOR CAPABILITY osgi.wiring.package:org.osgi.service.cm
+ REMOVED ATTRIBUTE bundle-version:1.9.10
+ ADDED ATTRIBUTE bundle-version:1.9.22
+ REMOVED VERSION 1.9.10
+ ADDED VERSION 1.9.22
+ MAJOR RESOURCE_ID org.apache.felix.eventadmin
+ MAJOR REQUIREMENTS <requirements>
+ MAJOR REQUIREMENT osgi.ee:JavaSE
+ REMOVED DIRECTIVE filter:(&(osgi.ee=JavaSE)(version=1.7))
+ ADDED DIRECTIVE filter:(&(osgi.ee=JavaSE)(version=1.8))
+ MAJOR CAPABILITIES <capabilities>
+ MAJOR CAPABILITY bnd.maven:org.apache.felix:org.apache.felix.eventadmin
+ REMOVED ATTRIBUTE maven-version:1.5.0
+ ADDED ATTRIBUTE maven-version:1.6.2
+ ADDED CAPABILITY osgi.content:445A90F6E31CDE9635C474CEA286273481D2E6EE293B52D8FC42ED8E927B5604
+ ADDED ATTRIBUTE mime:application/vnd.osgi.bundle
+ ADDED ATTRIBUTE size:83611
+ ADDED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/apache/felix/org.apache.felix.eventadmin/1.6.2/org.apache.felix.eventadmin-1.6.2.jar
+ REMOVED CAPABILITY osgi.content:A433A9020E1EAD82494AA6611E8A644F88733BD0278F349D6BEA3B2E448DDD71
+ REMOVED ATTRIBUTE mime:application/vnd.osgi.bundle
+ REMOVED ATTRIBUTE size:81529
+ REMOVED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/apache/felix/org.apache.felix.eventadmin/1.5.0/org.apache.felix.eventadmin-1.5.0.jar
+ MAJOR CAPABILITY osgi.identity:org.apache.felix.eventadmin
+ REMOVED ATTRIBUTE version:1.5.0
+ ADDED ATTRIBUTE version:1.6.2
+ MAJOR CAPABILITY osgi.wiring.bundle:org.apache.felix.eventadmin
+ REMOVED ATTRIBUTE bundle-version:1.5.0
+ ADDED ATTRIBUTE bundle-version:1.6.2
+ MAJOR CAPABILITY osgi.wiring.host:org.apache.felix.eventadmin
+ REMOVED ATTRIBUTE bundle-version:1.5.0
+ ADDED ATTRIBUTE bundle-version:1.6.2
+ MAJOR CAPABILITY osgi.wiring.package:org.osgi.service.event
+ REMOVED ATTRIBUTE bundle-version:1.5.0
+ ADDED ATTRIBUTE bundle-version:1.6.2
+ REMOVED VERSION 1.5.0
+ ADDED VERSION 1.6.2
+ REMOVED RESOURCE_ID org.osgi.util.promise
+ REMOVED REQUIREMENTS <requirements>
+ REMOVED REQUIREMENT osgi.ee:JavaSE
+ REMOVED DIRECTIVE filter:(&(osgi.ee=JavaSE)(version=1.7))
+ REMOVED REQUIREMENT osgi.wiring.package:org.osgi.util.function
+ REMOVED DIRECTIVE filter:(&(osgi.wiring.package=org.osgi.util.function)(version>=1.1.0)(!(version>=2.0.0)))
+ REMOVED CAPABILITIES <capabilities>
+ REMOVED CAPABILITY bnd.maven:org.osgi:org.osgi.util.promise
+ REMOVED ATTRIBUTE maven-classifier:
+ REMOVED ATTRIBUTE maven-extension:jar
+ REMOVED ATTRIBUTE maven-repository:Runtime
+ REMOVED ATTRIBUTE maven-version:1.1.1
+ REMOVED CAPABILITY osgi.content:4F85BECCD281CC1A4E735BD266A0DD3DB11651D3D0DDE001E6BFA55DBDFDEE83
+ REMOVED ATTRIBUTE mime:application/vnd.osgi.bundle
+ REMOVED ATTRIBUTE size:75587
+ REMOVED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/osgi/org.osgi.util.promise/1.1.1/org.osgi.util.promise-1.1.1.jar
+ REMOVED CAPABILITY osgi.identity:org.osgi.util.promise
+ REMOVED ATTRIBUTE copyright:Copyright (c) OSGi Alliance (2000, 2018). All Rights Reserved.
+ REMOVED ATTRIBUTE description:OSGi Companion Code for org.osgi.util.promise Version 1.1.1
+ REMOVED ATTRIBUTE documentation:https://www.osgi.org/
+ REMOVED ATTRIBUTE license:Apache-2.0; link="http://www.apache.org/licenses/LICENSE-2.0"; description="Apache License, Version 2.0"
+ REMOVED ATTRIBUTE type:osgi.bundle
+ REMOVED ATTRIBUTE version:1.1.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.bundle:org.osgi.util.promise
+ REMOVED ATTRIBUTE bundle-version:1.1.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.host:org.osgi.util.promise
+ REMOVED ATTRIBUTE bundle-version:1.1.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.package:org.osgi.util.promise
+ REMOVED ATTRIBUTE bnd.hashes:-1923478059
+ REMOVED ATTRIBUTE bundle-symbolic-name:org.osgi.util.promise
+ REMOVED ATTRIBUTE bundle-version:1.1.1.201810101357
+ REMOVED ATTRIBUTE version:1.1.1
+ REMOVED DIRECTIVE uses:org.osgi.util.function
+ REMOVED VERSION 1.1.1.201810101357
+ REMOVED RESOURCE_ID org.osgi.util.pushstream
+ REMOVED REQUIREMENTS <requirements>
+ REMOVED REQUIREMENT osgi.ee:JavaSE/compact1
+ REMOVED DIRECTIVE filter:(&(osgi.ee=JavaSE/compact1)(version=1.8))
+ REMOVED REQUIREMENT osgi.wiring.package:org.osgi.util.function
+ REMOVED DIRECTIVE filter:(&(osgi.wiring.package=org.osgi.util.function)(version>=1.1.0)(!(version>=2.0.0)))
+ REMOVED REQUIREMENT osgi.wiring.package:org.osgi.util.promise
+ REMOVED DIRECTIVE filter:(&(osgi.wiring.package=org.osgi.util.promise)(version>=1.1.0)(!(version>=2.0.0)))
+ REMOVED CAPABILITIES <capabilities>
+ REMOVED CAPABILITY bnd.maven:org.osgi:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE maven-classifier:
+ REMOVED ATTRIBUTE maven-extension:jar
+ REMOVED ATTRIBUTE maven-repository:Runtime
+ REMOVED ATTRIBUTE maven-version:1.0.1
+ REMOVED CAPABILITY osgi.content:1E0C9D435A107444A4461788E62BDDC94715E444AFDBC54417593ECA4BB50CE2
+ REMOVED ATTRIBUTE mime:application/vnd.osgi.bundle
+ REMOVED ATTRIBUTE size:132226
+ REMOVED ATTRIBUTE url:file:/Users/amit/.m2/repository/org/osgi/org.osgi.util.pushstream/1.0.1/org.osgi.util.pushstream-1.0.1.jar
+ REMOVED CAPABILITY osgi.identity:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE copyright:Copyright (c) OSGi Alliance (2000, 2018). All Rights Reserved.
+ REMOVED ATTRIBUTE description:OSGi Companion Code for org.osgi.util.pushstream Version 1.0.1
+ REMOVED ATTRIBUTE documentation:https://www.osgi.org/
+ REMOVED ATTRIBUTE license:Apache-2.0; link="http://www.apache.org/licenses/LICENSE-2.0"; description="Apache License, Version 2.0"
+ REMOVED ATTRIBUTE type:osgi.bundle
+ REMOVED ATTRIBUTE version:1.0.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.bundle:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE bundle-version:1.0.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.host:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE bundle-version:1.0.1.201810101357
+ REMOVED CAPABILITY osgi.wiring.package:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE bnd.hashes:-1923478059
+ REMOVED ATTRIBUTE bundle-symbolic-name:org.osgi.util.pushstream
+ REMOVED ATTRIBUTE bundle-version:1.0.1.201810101357
+ REMOVED ATTRIBUTE version:1.0.1
+ REMOVED DIRECTIVE uses:org.osgi.util.function,org.osgi.util.promise
+ REMOVED VERSION 1.0.1.201810101357
+ xref [options] <<jar path>> <[...]>
+[ -c --classes ] Show classes instead of packages[ -d --destination <string>* ] Match destination types[ -f --from ] Show references from other classes/packages (<)[ -j --java ] Include java.* packages[ -m --match <string>* ] Filter for class names, a globbing expression[ -n --nested ] Analyze nested JARs referenced via Bundle-ClassPath[ -r --referrredTo <string> ] Output list of package/class names that have been referred to[ -s --source <string>* ] Match source types[ -t --to ] Show references to other classes/packages (>) biz.aQute.bnd (master)$ bnd xref generated/*.jar
+ aQute.bnd.annotation >
+ aQute.bnd.annotation.component >
+ aQute.bnd.annotation.headers >
+ aQute.bnd.annotation.licenses >
+ aQute.bnd.annotation.metatype >
+ aQute.bnd.ant > aQute.service.reporter
+ org.apache.tools.ant
+ aQute.libg.reporter
+ org.apache.tools.ant.taskdefs
+ aQute.bnd.osgi
+ aQute.bnd.build
+ aQute.libg.qtokens
+ org.apache.tools.ant.types
+ aQute.bnd.osgi.eclipse
+ aQute.bnd.service.progress
+ aQute.bnd.service
+ aQute.bnd.version
+ aQute.bnd.build.model
+ aQute.bnd.build.model.clauses
+ aQute.bnd.build > aQute.service.reporter
+ aQute.bnd.osgi
+ aQute.bnd.service
+ aQute.libg.command
+ aQute.libg.sed
+ aQute.bnd.version
+ aQute.bnd.service.action
+ aQute.bnd.header
+ aQute.lib.io
+ aQute.libg.reporter
+ aQute.bnd.osgi.eclipse
+ aQute.bnd.help
+ aQute.lib.strings
+ aQute.libg.generics
+ aQute.bnd.maven.support
+ aQute.libg.glob
+ aQute.lib.converter
+ aQute.lib.collections
+ aQute.bnd.differ
+ aQute.bnd.service.diff
+ aQute.bnd.service.repository
+ aQute.lib.deployer
+ javax.naming
+ aQute.lib.hex
+ aQute.bnd.resource.repository
+ aQute.bnd.url
+ aQute.lib.settings
+ aQute.bnd.service.url
+ aQute.bnd.service.extension
+ aQute.bnd.build.model > aQute.bnd.build.model.conversions
+ aQute.libg.tuple
+ aQute.bnd.build.model.clauses
+ aQute.bnd.header
+ aQute.bnd.properties
+ aQute.bnd.build
+ aQute.bnd.version
+ aQute.lib.io
+ org.osgi.resource
+ aQute.bnd.osgi
+ aQute.bnd.build.model.clauses > aQute.bnd.header
+ aQute.bnd.build.model.conversions > aQute.bnd.header
+ aQute.libg.tuple
+ aQute.bnd.osgi
+ aQute.bnd.build.model
+ aQute.bnd.build.model.clauses
+ org.osgi.resource
+ aQute.bnd.osgi.resource
+ aQute.libg.qtokens
+ aQute.bnd.compatibility > aQute.bnd.osgi
+ aQute.bnd.component > aQute.bnd.osgi
+ aQute.service.reporter
+ aQute.lib.collections
+ org.osgi.service.component.annotations
+ aQute.bnd.version
+ aQute.bnd.component.error
+ aQute.lib.tag
+ aQute.bnd.header
+ aQute.bnd.service
+ aQute.bnd.component.error >
+ aQute.bnd.differ > aQute.bnd.header
+ aQute.bnd.service.diff
+ aQute.bnd.osgi
+ aQute.bnd.version
+ aQute.service.reporter
+ aQute.libg.generics
+ aQute.libg.cryptography
+ aQute.lib.hex
+ aQute.lib.io
+ aQute.lib.collections
+ aQute.bnd.annotation
+ aQute.bnd.service
+ aQute.bnd.enroute.commands > aQute.lib.getopt
+ aQute.bnd.osgi
+ aQute.service.reporter
+ aQute.bnd.main
+ aQute.bnd.build
+ aQute.lib.io
+ aQute.bnd.filerepo > aQute.bnd.version
+ aQute.bnd.header > aQute.bnd.version
+ aQute.bnd.osgi
+ aQute.service.reporter
+ aQute.libg.generics
+ aQute.libg.qtokens
+ aQute.lib.collections
+ aQute.bnd.help > aQute.bnd.osgi
+By default, xref only analyzes classes directly contained in the provided JAR files. Many OSGi bundles contain nested JARs that are referenced via the Bundle-ClassPath manifest header. To analyze these nested JARs as well, use the --nested option:
bnd xref --nested mybundle.jar
+This will:
+1. Parse the Bundle-ClassPath manifest header
+2. Extract and analyze any embedded JAR files
+3. Analyze directories referenced in the Bundle-ClassPath
+4. Include cross-references from all sources in the output
For example, given a bundle with the following structure:
+mybundle.jar
+├── META-INF/MANIFEST.MF (Bundle-ClassPath: .,lib/internal.jar)
+├── com/example/Main.class
+└── lib/internal.jar
+ └── com/example/internal/Helper.class
+Without --nested, only com.example.Main would be analyzed. With --nested, both com.example.Main and com.example.internal.Helper classes are included in the cross-reference analysis.
.*build-deps Stores build dependencies of bnd from gradle, ant, etc. In general, bnd will be among this. The files in this directory must be fully versionedExample: ~/.bnd/biz.aQute.bnd-2.2.0.jar
Pattern: .*
settings.json Contains the settings used by bnd in json format. These settings are maintained by bnd command line (bnd help settings). These settings can be used through macros and can provide passwords, user ids, and platform specific settings. Names starting witha dot (.) are considered protected
Example: {"id":"30...001","map":{".github.secret":"xxxxxx","github.user":"minime","email":"Peter.Kriens@aQute.biz"},"secret":"308...CC56"}
Pattern: .*
email The user's email address
.*id The public key for this machine
.*secret The private key for this machine
.*caches/shas Directory with sha artifacts. The sha is the name of the directory, it contains the artifact with a normal bsn-version.jar name
.*Example: Automatic-Module-Name: com.foo.bar
Pattern: [-\w]+(:?\.[-\w]+)*
Example: bin: target/classes
Pattern: .*
.*build-deps Stores build dependencies of bnd from gradle, ant, etc. In general, bnd will be among this. The files in this directory must be fully versionedExample: ~/.bnd/biz.aQute.bnd-2.2.0.jar
Pattern: .*
settings.json Contains the settings used by bnd in json format. These settings are maintained by bnd command line (bnd help settings). These settings can be used through macros and can provide passwords, user ids, and platform specific settings. Names starting witha dot (.) are considered protected
Example: {"id":"30...001","map":{".github.secret":"xxxxxx","github.user":"minime","email":"Peter.Kriens@aQute.biz"},"secret":"308...CC56"}
Pattern: .*
Options:
+email The user's email addressPattern: .*
id The public key for this machine
Pattern: .*
secret The private key for this machine
.*caches/shas Directory with sha artifacts. The sha is the name of the directory, it contains the artifact with a normal bsn-version.jar name
.*Example: Bnd-AddXMLToTest: a.xml
Pattern: .*
The Bnd-AddXmlToTest header allows you to include additional XML resources from the tested bundle in the output of a test report. This can be useful for adding custom metadata, configuration, or other relevant XML files to your test results.
When a test is executed, the test framework inspects the Bnd-AddXmlToTest header in the bundle under test. If present, it treats the value as a comma- or space-separated list of resource paths. For each specified resource:
In addition to including these resources, the test report also contains metadata about the test environment, such as: +- The hostname where the test ran +- The symbolic name and location of the tested bundle +- The timestamp and framework version +- System properties at the time of the test +- Information about all bundles in the framework, including their state, version, and symbolic name
+This mechanism provides a flexible way to enrich your test reports with bundle-specific XML data, making it easier to diagnose issues or provide additional context for test results.
+To use this feature, add the following header to your bundle’s manifest:
+Bnd-AddXmlToTest: config.xml, extra-info.xml
+When the tests run, both config.xml and extra-info.xml (if present in the bundle) will be included in the test report output.
TODO Needs review - AI Generated content
+.*The Bnd-LastModified header is automatically added by bnd to the bundle manifest. It contains a timestamp (in milliseconds since the epoch) that represents the aggregated last modified time of all resources included in the bundle. This value is useful for tracking when the bundle was last built or updated, and can help with cache invalidation or deployment automation.
The timestamp is generated at build time and reflects the most recent modification among all files and resources that are part of the bundle. This ensures that any change to the bundle's contents will result in a new, updated timestamp in the manifest.
+This header is set by bnd and should not be manually modified. It is primarily intended for tooling and automation purposes.
+TODO Needs review - AI Generated content
+Example: Bundle-ActivationPolicy: lazy
Values: lazy
Pattern: lazy
The Bundle-ActivationPolicy header specifies how the OSGi framework should activate the bundle once it has been started. The most common value for this header is lazy, which indicates that the bundle should be started in lazy activation mode. In this mode, the bundle's activator is not called until the first class from the bundle is loaded.
This header can also include additional directives, such as include and exclude, to further control activation behavior. The header is typically used as follows:
Bundle-ActivationPolicy: lazy
+For more details, see the OSGi Specification.
+If the header is set incorrectly (for example, with no argument or with too many arguments), bnd will issue a warning. The value should be set to lazy for standard lazy activation.
TODO Needs review - AI Generated content
+Example: Bundle-Activator: com.acme.foo.Activator
Values: ${classes;implementing;org.osgi.framework.BundleActivator}
Pattern: .*
The Bundle-Activator header specifies the fully qualified name of the class that implements the org.osgi.framework.BundleActivator interface. This class is used by the OSGi framework to start and stop the bundle. When the bundle is started, the framework creates an instance of this class and calls its start method. When the bundle is stopped, the stop method is called.
The activator class must be included in the bundle and accessible on the bundle classpath. If the class is missing, not accessible, or not properly implemented, bnd will issue a warning or error during analysis.
+Example:
+Bundle-Activator: com.example.MyActivator
+If the activator is not specified, the bundle will not have custom start/stop behavior.
+Example: Bundle-Blueprint: /blueprint/*.xml
Pattern: .*
The Bundle-Blueprint header is used to specify the location of Blueprint XML files within the bundle. These XML files define Blueprint dependency injection containers for OSGi. The header can list one or more resource paths, separated by commas. bnd will process these files and include them in the bundle as needed.
Example:
+Bundle-Blueprint: OSGI-INF/blueprint/context.xml, META-INF/spring/context.xml
+This header is typically used in bundles that provide OSGi Blueprint services or use Spring DM. The specified XML files must be present in the bundle at the given locations.
+TODO Needs review - AI Generated content
+Example: Bundle-Category: test
Values: osgi,test,game,util,eclipse,netbeans,jdk,specification
Pattern: .*
The Bundle-Category header allows you to specify one or more categories for your bundle. These categories can be used by tools and repositories to group and filter bundles. The header can be set using the BundleCategory annotation or directly in the manifest.
Example:
+Bundle-Category: utility, database
+Categories are free-form strings and can be customized as needed. This header is optional and is mainly used for documentation and discovery purposes.
+TODO Needs review - AI Generated content
+Example: Bundle-ClassPath: /lib/libnewgen.so, .
Pattern: .*
The Bundle-ClassPath header defines the internal class path for the bundle. It is a comma-separated list of JAR file paths or directories (inside the bundle) that contain classes and resources. The special entry . refers to the root of the bundle JAR and is the default if the header is not specified.
All files or directories listed in the Bundle-ClassPath must be present in the bundle. You can use the Include-Resource instruction to include additional JARs or directories. In most cases, it is recommended to avoid using Bundle-ClassPath unless necessary, as it can complicate class loading. Instead, use the @ option in Include-Resource to unroll JARs into the main bundle.
Example:
+Bundle-ClassPath: ., lib/extra.jar
+This header is important for advanced scenarios where you need to include additional classpath entries inside your bundle.
+TODO Needs review - AI Generated content
+Example: Bundle-ContactAddress: 2400 Oswego Road, Austin, TX 74563
Pattern: .*
Example: Bundle-Contributors: Peter.Kriens@aQute.biz;name='Peter Kriens Ing';organization=aQute;organizationUrl='http://www.aQute.biz';roles=ceo;timezone=+1
Pattern: .*
name The display name of the developerExample: name='Peter Kriens'
Pattern: .*
organization The display name of organization that employs the developer
Example: organization='aQute'
Pattern: .*
roles Roles played by the developer in this bundle's project (see Maven)
Example: roles=ceo
Pattern: .*
timezone Timezone in offset of UTC this developer usually resides in
Example: timezone+2
Pattern: .*
organizationUrl The URL of the developer's organization
Example: organizationURL='http://www.aQute.biz'
Pattern: .*
The Bundle-Contributors header lists contributors to the bundle, as defined in the Maven POM or via annotations. This header is not standardized by OSGi but is used for documentation and tracking purposes.
Example:
+Bundle-Contributors: John Doe;roles='developer';organization='Example Corp.'
+This header is optional and is mainly used for informational purposes.
+/*
+ * Bundle-Contributors header
+ */
+
+private void doBundleContributors(BundleContributors annotation) throws IOException {
+ StringBuilder sb = new StringBuilder(annotation.value());
+ if (annotation.name() != null) {
+ sb.append(";name='");
+ escape(sb, annotation.name());
+ sb.append("'");
+ }
+ if (annotation.roles() != null) {
+ sb.append(";roles='");
+ escape(sb,annotation.roles());
+ sb.append("'");
+ }
+ if (annotation.organizationUrl() != null) {
+ sb.append(";organizationUrl='");
+ escape(sb,annotation.organizationUrl());
+ sb.append("'");
+ }
+ if (annotation.organization() != null) {
+ sb.append(";organization='");
+ escape(sb,annotation.organization());
+ sb.append("'");
+ }
+ if (annotation.timezone() != 0)
+ sb.append(";timezone=").append(annotation.timezone());
+ add(Constants.BUNDLE_CONTRIBUTORS, sb.toString());
+}
+
+
+ /**
+ * Maven defines contributors and developers in the POM. This annotation will
+ * generate a (not standardized by OSGi) Bundle-Contributors header.
+ * <p>
+ * This annotation can be used directly on a type or it can 'color' an
+ * annotation. This coloring allows custom annotations that define a specific
+ * contributor. For example:
+ *
+ * <pre>
+ * {@code @}BundleContributor("Peter.Kriens@aQute.biz")
+ * {@code @}interface pkriens {}
+ *
+ * {@code @}pkriens
+ * public class MyFoo {
+ * ...
+ * }
+ * </pre>
+ *
+ * Duplicates are removed before the header is generated and the coloring does
+ * not create an entry in the header, only an annotation on an actual type is
+ * counted. This makes it possible to make a library of contributors without
+ * then adding them all to the header.
+ * <p>
+ * See <a href=https://maven.apache.org/pom.html#Developers>Maven POM reference</a>
+ */
+ @Retention(RetentionPolicy.CLASS)
+ @Target({
+ ElementType.ANNOTATION_TYPE, ElementType.TYPE
+ })
+ public @interface BundleContributors {
+
+ /**
+ * The email address of the developer.
+ */
+ String value();
+
+ /**
+ * The display name of the developer. If not specified, the {@link #value()}
+ * is used.
+ */
+ String name() default "";
+
+ /**
+ * The roles this contributor plays in the development.
+ */
+ String[] roles() default {};
+
+ /**
+ * The name of the organization where the contributor works for.
+ */
+ String organization() default "";
+
+ /**
+ * The url of the organization where the contributor works for.
+ */
+ String organizationUrl() default "";
+
+ /**
+ * Time offset in hours from UTC without Daylight savings
+ */
+ int timezone() default 0;
+ }
+TODO Needs review - AI Generated content
+Example: Bundle-Copyright: OSGi (c) 2002
Pattern: .*
The Bundle-Copyright header specifies the copyright statement for the bundle. This information can be set using the BundleCopyright annotation or directly in the manifest. It is intended for informational purposes and does not affect bundle behavior.
Example:
+Bundle-Copyright: Copyright (c) 2025 Example Corp.
+This header is optional and is typically used to document the legal ownership of the bundle's contents.
+TODO Needs review - AI Generated content
+Example: Bundle-Description: Ceci ce n'est pas une bundle
Pattern: .*
The Bundle-Description header provides a short, human-readable description of the bundle. This description is intended to help users and tools understand the purpose or functionality of the bundle at a glance.
Example:
+Bundle-Description: Provides OSGi-based logging services for applications.
+This header is optional but recommended for clarity and documentation purposes.
+TODO Needs review - AI Generated content
+Example: Bundle-Developers: Peter.Kriens@aQute.biz;name='Peter Kriens Ing';organization=aQute;organizationUrl='http://www.aQute.biz';roles=ceo;timezone=+1
Pattern: .*
The Bundle-Developers header lists developers of the bundle, as defined in the Maven POM or via annotations. This header is not standardized by OSGi but is used for documentation and tracking purposes.
Example:
+Bundle-Developers: Jane Smith;roles='lead';organization='Example Corp.'
+This header is optional and is mainly used for informational purposes.
+/*
+ * Bundle-Developers header
+ */
+private void doBundleDevelopers(BundleDevelopers annotation) throws IOException {
+ StringBuilder sb = new StringBuilder(annotation.value());
+ if (annotation.name() != null) {
+ sb.append(";name='");
+ escape(sb, annotation.name());
+ sb.append("'");
+ }
+ if (annotation.roles() != null) {
+ sb.append(";roles='");
+ escape(sb,annotation.roles());
+ sb.append("'");
+ }
+ if (annotation.organizationUrl() != null) {
+ sb.append(";organizationUrl='");
+ escape(sb,annotation.organizationUrl());
+ sb.append("'");
+ }
+ if (annotation.organization() != null) {
+ sb.append(";organization='");
+ escape(sb,annotation.organization());
+ sb.append("'");
+ }
+ if (annotation.timezone() != 0)
+ sb.append(";timezone=").append(annotation.timezone());
+
+ add(Constants.BUNDLE_DEVELOPERS, sb.toString());
+}
+
+
+ /**
+ * Maven defines developers and developers in the POM. This annotation will
+ * generate a (not standardized by OSGi) Bundle-Developers header.
+ * <p>
+ * A deve
+ * <p>
+ * This annotation can be used directly on a type or it can 'color' an
+ * annotation. This coloring allows custom annotations that define a specific
+ * developer. For example:
+ *
+ * <pre>
+ * @BundleContributor("Peter.Kriens@aQute.biz")
+ * @interface pkriens {}
+ *
+ * @pkriens
+ * public class MyFoo {
+ * ...
+ * }
+ * </pre>
+ *
+ * Duplicates are removed before the header is generated and the coloring does
+ * not create an entry in the header, only an annotation on an actual type is
+ * counted. This makes it possible to make a library of developers without
+ * then adding them all to the header.
+ * <p>
+ * {@see https://maven.apache.org/pom.html#Developers}
+ */
+ @Retention(RetentionPolicy.CLASS)
+ @Target({
+ ElementType.ANNOTATION_TYPE, ElementType.TYPE
+ })
+ public @interface BundleDevelopers {
+
+ /**
+ * The email address of the developer.
+ */
+ String value();
+
+ /**
+ * The display name of the developer. If not specified, the {@link #value()}
+ * is used.
+ */
+ String name() default "";
+
+ /**
+ * The roles this developer plays in the development.
+ */
+ String[] roles() default {};
+
+ /**
+ * The name of the organization where the developer works for.
+ */
+ String organization() default "";
+
+ /**
+ * The url of the organization where the developer works for.
+ */
+ String organizationUrl() default "";
+
+ /**
+ * Time offset in hours from UTC without Daylight savings
+ */
+ int timezone() default 0;
+ }
+TODO Needs review - AI Generated content
+Example: Bundle-DocURL: http://www.aQute.biz/Code/Bnd
Pattern: .*
The Bundle-DocURL header specifies a URL that points to documentation for the bundle. This can be a website, wiki page, or any other resource that provides more information about the bundle's usage, features, or configuration.
Example:
+Bundle-DocURL: https://docs.example.com/my-bundle
+Providing this header is optional but highly recommended for discoverability and support.
+/*
+ * Bundle-DocURL header
+ */
+private void doBundleDocURL(BundleDocURL annotation) {
+ add(Constants.BUNDLE_DOCURL, annotation.value());
+}
+TODO Needs review - AI Generated content
+Example: Bundle-Icon: /icons/bnd.png;size=64
Values: /icons/bundle.png
Pattern: .*
size Icons size in pixels, e.g. 64.Example: size=64
Values: 16,32,48,64,128
Pattern: \d+
Example: Bundle-License: http://www.opensource.org/licenses/jabberpl.php
Values: http://www.apache.org/licenses/LICENSE-2.0,<<EXTERNAL>>
Pattern: (.*|<<EXTERNAL>>)
description Human readable description of the license.Example: description="Describe the license here"
Pattern: .*
link
Example: ``
+Pattern: .*
The Bundle-License header provides machine-readable license information for the bundle. It can list one or more licenses, separated by commas, and may include additional attributes such as a description or a link to the license text.
Example:
+Bundle-License: Apache-2.0;description='Apache License, Version 2.0';link='https://www.apache.org/licenses/LICENSE-2.0'
+This header is optional but recommended for clarity and compliance. It helps users and tools understand the licensing terms of the bundle.
+ /*
+ * Bundle-License header
+ */
+private void doLicense(BundleLicense annotation) {
+ StringBuilder sb = new StringBuilder(annotation.name());
+ if (!annotation.description().equals(""))
+ sb.append(";description='").append(annotation.description().replaceAll("'", "\\'")).append("'");
+ if (!annotation.link().equals(""))
+ sb.append(";link='").append(annotation.link().replaceAll("'", "\\'")).append("'");
+ add(Constants.BUNDLE_LICENSE, sb.toString());
+}
+TODO Needs review - AI Generated content
+Example: Bundle-Localization: OSGI-INF/l10n/bundle
Values: OSGI-INF/l10n/bundle
Pattern: .*
Example: # Bundle-ManifestVersion: 2
Values: 2
Pattern: \d+
The Bundle-ManifestVersion header is always set to 2 for OSGi R4 and later bundles. This value is required by the OSGi specification and cannot be changed or omitted. It ensures compatibility with the OSGi framework.
Example:
+Bundle-ManifestVersion: 2
+This header is automatically set by bnd and should not be modified manually.
+TODO Needs review - AI Generated content
+Example: Bundle-Name: My Bundle
Pattern: .*
The Bundle-Name header defines a short, human-readable name for the bundle. This name is intended for display in user interfaces and tools. If the Bundle-Name is not set, it will default to the value of the Bundle-SymbolicName header.
Example:
+Bundle-Name: My Example Bundle
+This header is optional but recommended for clarity and usability.
+If the Bundle-Name is not set, it will default to the Bundle-SymbolicName.
+ //
+ // Use the same name for the bundle name as BSN when
+ // the bundle name is not set
+ //
+ if (main.getValue(BUNDLE_NAME) == null) {
+ main.putValue(BUNDLE_NAME, bsn);
+ }
+TODO Needs review - AI Generated content
+Example: Bundle-NativeCode: /lib/http.DLL; osname = QNX; osversion = 3.1
Pattern: .*
osname The name of the operating system.Example: osname=MacOS
Values: AIX,DigitalUnix,Embos,Epoc32,FreeBSD,HPUX,IRIX,Linux,MacOS,NetBSD,Netware,OpenBSD,OS2,QNX,Solaris,SunOS,VxWorks,Windows95,Win32,Windows98,WindowsNT,WindowsCE,Windows2000,Windows2003,WindowsXP,WindowsVista
Pattern: .*
osversion Operating System Version.
Example: osversion=3.1
Pattern: .*
language Language ISO 639 code.
Example: language=nl
Pattern: \p{Upper}{2}
processor Processor name.
Example: processor=x86
Values: 68k,ARM_LE,arm_le,arm_be,Alpha,ia64n,ia64w,Ignite,Mips,PArisc,PowerPC,Sh4,Sparc,Sparcv9,S390,S390x,V850E,x86,i486,x86-64
Pattern: .*
selection-filter The value of this attribute must be a filter expression that indicates if the native code clause should be selected or not.
Example: selection-filter="(com.acme.windowing=win32)"
Pattern: .*
The Bundle-NativeCode header specifies native code libraries that are included in the bundle and may be loaded by the OSGi framework. This header lists one or more native code clauses, each describing the path to a native library and optional attributes such as operating system, processor, language, or version constraints.
Example:
+Bundle-NativeCode: lib/linux-x86/libfoo.so;osname=Linux;processor=x86, lib/win32-x86/foo.dll;osname=Windows;processor=x86
+A wildcard (*) can be used as the last entry to indicate that the bundle can run without native code if no match is found. If a required native library is missing or not found in the JAR, bnd will issue an error.
This header is used for bundles that need to provide platform-specific native libraries alongside Java code.
+TODO Needs review - AI Generated content
+Example: Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0
Values: OSGi/Minimum-1.0,OSGi/Minimum-1.1,OSGi/Minimum-1.2,JRE-1.1,J2SE-1.2,J2SE-1.3,J2SE-1.4,J2SE-1.5,JavaSE-1.6,JavaSE-1.7,JavaSE/compact1-1.8,JavaSE/compact2-1.8,JavaSE/compact3-1.8,JavaSE-1.8,JavaSE-9,JavaSE-10,JavaSE-11,JavaSE-12,JavaSE-13,JavaSE-14,JavaSE-15,JavaSE-16,JavaSE-17,JavaSE-18,JavaSE-19,JavaSE-20,JavaSE-21,JavaSE-22,JavaSE-23,JavaSE-24,JavaSE-25,JavaSE-26,JavaSE-27,JavaSE-28,JavaSE-29,JavaSE-30,JavaSE-31,JavaSE-32,JavaSE-33,JavaSE-34,JavaSE-35,JavaSE-36,JavaSE-37,JavaSE-38,JavaSE-39,JavaSE-40,JavaSE-41,JavaSE-42,JavaSE-43,JavaSE-44,JavaSE-45,JavaSE-46,JavaSE-47,JavaSE-48,JavaSE-49,JavaSE-50,CDC-1.0/Foundation-1.0,CDC-1.1/Foundation-1.1,PersonalJava-1.1,PersonalJava-1.2,CDC-1.0/PersonalBasis-1.0,CDC-1.0/PersonalJava-1.0,CDC-1.1/PersonalBasis-1.1,CDC-1.1/PersonalJava-1.1
Pattern: .*
The Bundle-RequiredExecutionEnvironment header lists the execution environments required by the bundle, separated by commas. These environments must be present on the OSGi framework for the bundle to resolve. This header is deprecated in recent OSGi specifications.
Example:
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8, OSGi/Minimum-1.2
+This header is optional and mainly used for legacy compatibility.
+ verifyListHeader(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, EENAME, false);
+
+ final static Pattern EENAME = Pattern.compile("CDC-1\\.0/Foundation-1\\.0" + "|CDC-1\\.1/Foundation-1\\.1"
+ + "|OSGi/Minimum-1\\.[1-9]" + "|JRE-1\\.1" + "|J2SE-1\\.2" + "|J2SE-1\\.3"
+ + "|J2SE-1\\.4" + "|J2SE-1\\.5" + "|JavaSE-1\\.6" + "|JavaSE-1\\.7"
+ + "|JavaSE-1\\.8" + "|PersonalJava-1\\.1" + "|PersonalJava-1\\.2"
+ + "|CDC-1\\.0/PersonalBasis-1\\.0" + "|CDC-1\\.0/PersonalJava-1\\.0");
+ final static EE[] ees = {
+ new EE("CDC-1.0/Foundation-1.0", V1_3, V1_1),
+ new EE("CDC-1.1/Foundation-1.1", V1_3, V1_2),
+ new EE("OSGi/Minimum-1.0", V1_3, V1_1),
+ new EE("OSGi/Minimum-1.1", V1_3, V1_2),
+ new EE("JRE-1.1", V1_1, V1_1), //
+ new EE("J2SE-1.2", V1_2, V1_1), //
+ new EE("J2SE-1.3", V1_3, V1_1), //
+ new EE("J2SE-1.4", V1_3, V1_2), //
+ new EE("J2SE-1.5", V1_5, V1_5), //
+ new EE("JavaSE-1.6", V1_6, V1_6), //
+ new EE("PersonalJava-1.1", V1_1, V1_1), //
+ new EE("JavaSE-1.7", V1_7, V1_7), //
+ new EE("JavaSE-1.8", V1_8, V1_8), //
+ new EE("PersonalJava-1.1", V1_1, V1_1), //
+ new EE("PersonalJava-1.2", V1_1, V1_1), new EE("CDC-1.0/PersonalBasis-1.0", V1_3, V1_1),
+ new EE("CDC-1.0/PersonalJava-1.0", V1_3, V1_1), new EE("CDC-1.1/PersonalBasis-1.1", V1_3, V1_2),
+ new EE("CDC-1.1/PersonalJava-1.1", V1_3, V1_2)
+ };
+TODO Needs review - AI Generated content
+Example: Bundle-SCM: url=https://github.com/bndtools/bnd, connection=scm:git:https://github.com/bndtools/bnd.git, developerConnection=scm:git:git@github.com:bndtools/bnd.git
Pattern: .*
Example: Bundle-SymbolicName: com.acme.foo.daffy;singleton:=true
Values: ${p}
Pattern: [-\w]+(:?\.[-\w]+)*
singleton: Indicates that the bundle can only have a single version resolved. A value of true indicates that the bundle is a singleton bundle. The default value is false. The Framework must resolve at most one bundle when multiple versions of a singleton bundle with the same symbolic name are installed. Singleton bundles do not affect the resolution of non-singleton bundles with the same symbolic name.Example: singleton:=false
Values: true,false
Pattern: true|false|TRUE|FALSE
fragment-attachment: Defines how fragments are allowed to be attached, see the fragments in Fragment Bundles on page 73. The following values are valid for this directive:
Example: ``
+Values: always|never|resolve-time
Pattern: always|never|resolve-time
blueprint.wait-for-dependencies
Example: ``
+Values: true,false
Pattern: true|false|TRUE|FALSE
blueprint.timeout
Example: ``
+Values: 30000,60000,300000
Pattern: \d+
The Bundle-SymbolicName header specifies a unique, non-localizable name for the bundle. This name, together with the version, must uniquely identify a bundle, even if it is installed multiple times in a framework. The symbolic name should follow the reverse domain name convention (e.g., com.example.mybundle).
If not set, bnd will use the name of the main bnd file or, if the file is called bnd.bnd, the name of its directory. The ${project} variable can also be used to set this value.
Example:
+Bundle-SymbolicName: com.example.mybundle
+This header is required for all OSGi bundles.
+The Bundle-SymbolicName header can be set by the user. The default is the name of the main bnd file, or if the main bnd file is called bnd.bnd, it will be the name of the directory of the bnd file. An interesting variable is ${project} that will be set to this default name.
+ private void verifySymbolicName() {
+ Parameters bsn = parseHeader(main.get(Analyzer.BUNDLE_SYMBOLICNAME));
+ if (!bsn.isEmpty()) {
+ if (bsn.size() > 1)
+ error("More than one BSN specified " + bsn);
+
+ String name = bsn.keySet().iterator().next();
+ if (!isBsn(name)) {
+ error("Symbolic Name has invalid format: " + name);
+ }
+ }
+}
+
+ /**
+ * @param name
+ * @return
+ */
+public static boolean isBsn(String name) {
+ return SYMBOLICNAME.matcher(name).matches();
+}
+
+
+public final static String SYMBOLICNAME_STRING = "[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*";
+TODO Needs review - AI Generated content
+Example: Bundle-UpdateLocation: http://www.acme.com/Firewall/bundle.jar
Pattern: .*
Example: Bundle-Vendor: OSGi Alliance
Pattern: .*
Example: Bundle-Version: 1.23.4.build200903221000
Pattern: \d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The Bundle-Version header specifies the version of the bundle. If this header is not provided, a default version of 0 will be set. The version must follow the OSGi versioning scheme: major.minor.micro.qualifier.
Example:
+Bundle-Version: 1.2.3
+This header is important for managing updates and dependencies between bundles.
+The version of the bundle. If no such header is provided, a version of 0 will be set.
+ verifyHeader(Constants.BUNDLE_VERSION, VERSION, true);
+ public final static Pattern VERSION = Pattern.compile(VERSION_STRING);
+ public final static String VERSION_STRING = "[0-9]{1,9}(\\.[0-9]{1,9}(\\.[0-9]{1,9}(\\.[0-9A-Za-z_-]+)?)?)?";
+
+
+ /**
+ * Intercept the call to analyze and cleanup versions after we have analyzed
+ * the setup. We do not want to cleanup if we are going to verify.
+ */
+
+@Override
+public void analyze() throws Exception {
+ super.analyze();
+ cleanupVersion(getImports(), null);
+ cleanupVersion(getExports(), getVersion());
+ String version = getProperty(BUNDLE_VERSION);
+ if (version != null) {
+ version = cleanupVersion(version);
+ if (version.endsWith(".SNAPSHOT")) {
+ version = version.replaceAll("SNAPSHOT$", getProperty(SNAPSHOT, "SNAPSHOT"));
+ }
+ setProperty(BUNDLE_VERSION, version);
+ }
+}
+
+
+ if (main.getValue(BUNDLE_VERSION) == null)
+ main.putValue(BUNDLE_VERSION, "0");
+TODO Needs review - AI Generated content
+Example: Conditional-Package: com.*
Values: ${packages}
Pattern: .*
This instruction is equal to using -conditionalpackage except for the fact that the header in addition will be copied into the generated bundle manifest (like all headers beginning with a capital letter).
+The Conditional-Package header allows you to specify package patterns that, when referred to by your code, will be included in the bundle if they match the given specifications. This is useful for conditionally including packages from the classpath based on actual usage.
Example:
+Conditional-Package: com.example.optional.*
+bnd will recursively add matching packages until no more additions are found. This header is useful for advanced packaging scenarios.
+/**
+ * Answer extra packages. In this case we implement conditional package. Any
+ */
+@Override
+protected Jar getExtra() throws Exception {
+ Parameters conditionals = getParameters(CONDITIONAL_PACKAGE);
+ conditionals.putAll(getParameters(CONDITIONALPACKAGE));
+ if (conditionals.isEmpty())
+ return null;
+ trace("do Conditional Package %s", conditionals);
+ Instructions instructions = new Instructions(conditionals);
+
+ Collection<PackageRef> referred = instructions.select(getReferred().keySet(), false);
+ referred.removeAll(getContained().keySet());
+
+ Jar jar = new Jar("conditional-import");
+ addClose(jar);
+ for (PackageRef pref : referred) {
+ for (Jar cpe : getClasspath()) {
+ Map<String,Resource> map = cpe.getDirectories().get(pref.getPath());
+ if (map != null) {
+ copy(jar, cpe, pref.getPath(), false);
+ // Now use copy so that bnd.info is processed, next line
+ // should be
+ // removed in the future TODO
+ // jar.addDirectory(map, false);
+ break;
+ }
+ }
+ }
+ if (jar.getDirectories().size() == 0) {
+ trace("extra dirs %s", jar.getDirectories());
+ return null;
+ }
+ return jar;
+}
+TODO Needs review - AI Generated content
+.*The Created-By header in the bundle manifest records the Java version and vendor used to build the bundle. This information is automatically added by bnd during the build process. It can be useful for tracking the build environment, diagnosing compatibility issues, or auditing builds.
The value typically looks like:
+Created-By: 17.0.8 (Eclipse Adoptium)
+This header is set by bnd and should not be manually changed.
+The Created-By header is important for understanding the context in which a Java bundle was created. It provides insight into the Java Development Kit (JDK) version and the vendor's implementation details. This can be critical when you are trying to debug issues, ensure compatibility with other Java components, or verify that the bundle was built using a trusted JDK vendor.
TODO Needs review - AI Generated content
+Example: DynamicImport-Package: com.acme.plugin.*
Values: ``
+Pattern: .*
version A version range to select the version of an export definition. The default value is 0.0.0.Example: version="[1.2,3.0)"
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
bundle-symbolic-name The bundle symbolic name of the exporting bundle.
Example: bundle-symbolic-name=com.acme.foo.daffy
Pattern: [-\w]+(:?\.[-\w]+)*
bundle-version A version range to select the bundle version of the exporting bundle. The default value is 0.0.0.
Example: bundle-version=1.3
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The DynamicImport-Package header allows a bundle to declare packages that should be imported dynamically at runtime, rather than being resolved at deployment time. This is useful for cases where the set of required packages is not known in advance.
The header contains a comma-separated list of package names, which can include wildcards and directives. For example:
+DynamicImport-Package: com.example.*, org.osgi.service.*
+Packages listed here will be resolved by the OSGi framework when they are first referenced by the bundle. Use this feature sparingly, as it can make dependency management less predictable.
+/**
+ * <pre>
+ * DynamicImport-Package ::= dynamic-description
+ * ( ',' dynamic-description )*
+ *
+ * dynamic-description::= wildcard-names ( ';' parameter )*
+ * wildcard-names ::= wildcard-name ( ';' wildcard-name )*
+ * wildcard-name ::= package-name
+ * | ( package-name '.*' ) // See 1.4.2
+ * | '*'
+ * </pre>
+ */
+private void verifyDynamicImportPackage() {
+ verifyListHeader(Constants.DYNAMICIMPORT_PACKAGE, WILDCARDPACKAGE, true);
+ String dynamicImportPackage = get(Constants.DYNAMICIMPORT_PACKAGE);
+ if (dynamicImportPackage == null)
+ return;
+
+ Parameters map = main.getDynamicImportPackage();
+ for (String name : map.keySet()) {
+ name = name.trim();
+ if (!verify(name, WILDCARDPACKAGE))
+ error(Constants.DYNAMICIMPORT_PACKAGE + " header contains an invalid package name: " + name);
+
+ Map<String,String> sub = map.get(name);
+ if (r3 && sub.size() != 0) {
+ error("DynamicPackage-Import has attributes on import: " + name
+ + ". This is however, an <=R3 bundle and attributes on this header were introduced in R4. ");
+ }
+ }
+}
+TODO Needs review - AI Generated content
+Example: Export-Package: org.osgi.util.tracker;version=1.3
Values: ${packages}
Pattern: .*
-noimport: By default, bnd makes all exports also imports. Adding a -noimport: to an exported package will make it export only.Example: -noimport:=true
Values: true,false
Pattern: true|false|TRUE|FALSE
uses: Calculated by bnd: It is a comma-separated list of package names that are used by the exported package.
Example: Is calculated by bnd
Pattern: .*
mandatory: A comma-separated list of attribute names. Note that the use of a comma in the value requires it to be enclosed in double quotes. A bundle importing the package must specify the mandatory attributes, with a value that matches, to resolve to the exported package.
Example: mandatory:="bar,foo"
Pattern: .*
include: A comma-separated list of class names that must be visible to an importer.
Example: include:="Qux*"
Pattern: .*
exclude: A comma-separated list of class names that must not be visible to an importer.
Example: exclude:="QuxImpl*,BarImpl"
Pattern: .*
-import: Experimental.
Example: ``
+Pattern: .*
The Export-Package header declares which Java packages in the bundle are made available to other bundles. You can use patterns, wildcards, and directives to control which packages are exported and how.
Example:
+Export-Package: !com.*, *
+This example exports all packages except those starting with com.
Note: By default bnd automatically calculates Import-Package references for exported packages. This is called package substitution
+You can use the -noimport:=true directive which instructs bnd to not calculate Import-Package references for exported packages.
Example:
+`
+Export-Package: com.*;-noimport:=true
With the -nosubstitution: true instruction, this substitution behavior can be disabled globally.
+See also: -exportcontents.
The bnd definition allows the specification to be done using ''patterns'', a modified regular expression. All patterns in the definition are matched against every package on the [ class path][#CLASSPATH ]. If the pattern is a negating pattern (starts with !) and it is matched, then the package is completely excluded.
+Normal patterns cause the package to be included in the resulting bundle. Patterns can include both directives and attributes, these items will be copied to the output. The list is ordered, earlier patterns take effect before later patterns.
+The following examples copies everything on the class path except for packages starting with com. If the source packages have an associated version (from their manifest of packageinfo file), then this version is automatically added to the clauses.
Export-Package= !com.*, *
+Exports are automatically imported. This features can be disabled with a special directive on the export instruction: -noimport:=true. For example:
Export-Package= com.acme.impl.*;-noimport:=true, *
+Bnd will automatically calculate the uses: directive. This directive is used by the OSGi framework to create a consistent class space for a bundle. The Export-Package statement allows this directive to be overridden on a package basis by specifying the directive in an Export-Package instruction.
Export-package = com.acme.impl.*;uses="my.special.import"
+However, in certain cases it is necessary to augment the uses clause. It is therefore possible to use the special name <<USES>> in the clause. Bnd will replace this special name with the calculated uses set. Bnd will remove any extraneous commas when the <<USES>> is empty.
Export-package = com.acme.impl.*;uses:="my.special.import,<<USES>>"
+Directives that are not part of the OSGi specification will give a warning unless they are prefixed with a 'x-'.
+When bnd processes the Export-Package header, it automatically ensures consistent ordering of attributes and directives within each package clause. Attributes (keys without a trailing colon) are placed before directives (keys with a trailing colon), and both groups are sorted alphabetically. This ensures reproducible builds and easier comparison of manifest files.
For more details, see OSGi Header Attribute and Directive Ordering.
+ //
+ // EXPORTS
+ //
+ {
+ Set<Instruction> unused = Create.set();
+
+ Instructions filter = new Instructions(getExportPackage());
+ filter.append(getExportContents());
+
+ exports = filter(filter, contained, unused);
+
+ if (!unused.isEmpty()) {
+ warning("Unused " + Constants.EXPORT_PACKAGE + " instructions: %s ", unused);
+ }
+
+ // See what information we can find to augment the
+ // exports. I.e. look on the classpath
+ augmentExports(exports);
+ }
+
+ /**
+ * Check if the given resource is in scope of this bundle. That is, it
+ * checks if the Include-Resource includes this resource or if it is a class
+ * file it is on the class path and the Export-Package or Private-Package
+ * include this resource.
+ *
+ * @param f
+ * @return
+ */
+public boolean isInScope(Collection<File> resources) throws Exception {
+ Parameters clauses = parseHeader(getProperty(Constants.EXPORT_PACKAGE));
+ clauses.putAll(parseHeader(getProperty(Constants.PRIVATE_PACKAGE)));
+ clauses.putAll(parseHeader(getProperty(Constants.PRIVATEPACKAGE)));
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ clauses.putAll(parseHeader(getProperty(Constants.TESTPACKAGES, "test;presence:=optional")));
+ }
+
+ Collection<String> ir = getIncludedResourcePrefixes();
+
+ Instructions instructions = new Instructions(clauses);
+
+ for (File r : resources) {
+ String cpEntry = getClasspathEntrySuffix(r);
+
+ if (cpEntry != null) {
+
+ if (cpEntry.equals("")) // Meaning we actually have a CPE
+ return true;
+
+ String pack = Descriptors.getPackage(cpEntry);
+ Instruction i = matches(instructions, pack, null, r.getName());
+ if (i != null)
+ return !i.isNegated();
+ }
+
+ // Check if this resource starts with one of the I-C header
+ // paths.
+ String path = r.getAbsolutePath();
+ for (String p : ir) {
+ if (path.startsWith(p))
+ return true;
+ }
+ }
+ return false;
+}
+
+ /**
+ * Verify that the exports only use versions.
+ */
+private void verifyExports() {
+ if (isStrict()) {
+ Parameters map = parseHeader(manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
+ Set<String> noexports = new HashSet<String>();
+
+ for (Entry<String,Attrs> e : map.entrySet()) {
+
+ String version = e.getValue().get(Constants.VERSION_ATTRIBUTE);
+ if (version == null) {
+ noexports.add(e.getKey());
+ } else {
+ if (!VERSION.matcher(version).matches()) {
+ Location location;
+ if (VERSIONRANGE.matcher(version).matches()) {
+ location = error(
+ "Export Package %s version is a range: %s; Exports do not allow for ranges.",
+ e.getKey(), version).location();
+ } else {
+ location = error("Export Package %s version has invalid syntax: %s", e.getKey(), version)
+ .location();
+ }
+ location.header = Constants.EXPORT_PACKAGE;
+ location.context = e.getKey();
+ }
+ }
+
+ if (e.getValue().containsKey(Constants.SPECIFICATION_VERSION)) {
+ Location location = error(
+ "Export Package %s uses deprecated specification-version instead of version", e.getKey())
+ .location();
+ location.header = Constants.EXPORT_PACKAGE;
+ location.context = e.getKey();
+ }
+
+ String mandatory = e.getValue().get(Constants.MANDATORY_DIRECTIVE);
+ if (mandatory != null) {
+ Set<String> missing = new HashSet<String>(split(mandatory));
+ missing.removeAll(e.getValue().keySet());
+ if (!missing.isEmpty()) {
+ Location location = error("Export Package %s misses mandatory attribute: %s", e.getKey(),
+ missing).location();
+ location.header = Constants.EXPORT_PACKAGE;
+ location.context = e.getKey();
+ }
+ }
+ }
+
+ if (!noexports.isEmpty()) {
+ Location location = error("Export Package clauses without version range: %s", noexports).location();
+ location.header = Constants.EXPORT_PACKAGE;
+ }
+ }
+}
+TODO Needs review - AI Generated content
+Example: Export-Service: org.osgi.service.log.LogService
Values: ${classes;implementing;*}
Pattern: .*
Example: Fragment-Host: org.eclipse.swt; bundle-version="[3.0.0,4.0.0)"
Pattern: .*
extension: Indicates this extension is a system or boot class path extension. It is only applicable when the Fragment-Host is the System Bundle.Example: extension:=framework
Values: framework,bootclasspath
Pattern: framework|bootclasspath
bundle-version A version range to select the bundle version of the exporting bundle. The default value is 0.0.0.
Example: bundle-version=1.3
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The Fragment-Host header specifies the symbolic name of the host bundle(s) for a fragment bundle. A fragment is attached to its host at runtime and shares the same class loader. When a fragment is attached, some headers (such as Import-Package) are merged with those of the host.
Example:
+Fragment-Host: com.example.hostbundle
+bnd will automatically subtract any packages found in the host from the fragment's imports. This header is required for all fragment bundles.
+The Fragment-Host manifest header links the fragment to its potential hosts. A fragment bundle is loaded in the +same class loader as the host that it will be attached to in runtime. When a fragment is attached to its host, +then some headers are merged. One of those headers is the Import Package header.
+bnd will calculate the references without taking the host into account. If the fragment uses packages from the host, +quite likely, then these would result in imports. For this reason, bnd will subtract any package that can be found +in the host from the import.
+TODO Needs review - AI Generated content
+Example: Ignore-Package: com.foo.bar
Values: ${packages}
Pattern: .*
Example: Import-Package: !com.exotic.*, com.acme.foo;vendor=ACME, *
Values: ${exported_packages}
Pattern: .*
-remove-attribute: Remove the given attributes from matching imported packages.Example: -remove-attribute:=foo.*
Pattern: .*
resolution: Indicates that the packages must be resolved if the value is mandatory, which is the default. If mandatory packages cannot be resolved, then the bundle must fail to resolve. A value of optional indicates that the packages are optional.
Example: resolution:=optional
Values: mandatory,optional
Pattern: mandatory|optional
version A version range to select the version of an export definition. The default value is 0.0.0.
Example: version="[1.2,3.0)"
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
bundle-symbolic-name The bundle symbolic name of the exporting bundle.
Example: bundle-symbolic-name=com.acme.foo.daffy
Pattern: [-\w]+(:?\.[-\w]+)*
bundle-version A version range to select the bundle version of the exporting bundle. The default value is 0.0.0.
Example: bundle-version=1.3
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The Import-Package header lists the Java packages that the bundle requires from other bundles. By default, bnd will import all referred packages, but you can use patterns and negations to control which packages are imported.
Example:
+Import-Package: !com.example.internal.*, *
+This example imports all referred packages except those starting with com.example.internal. You can also add explicit imports for packages not directly referred to by your code.
bnd will attempt to find the exported version of imported packages and will use the exported version unless a specific version or range is specified. This header is important for managing dependencies between bundles.
+The Import-Package header lists the packages that are required by the contained packages. The default for this header is *, resulting in importing all referred packages. This header therefore rarely has to be specified. However, in certain cases there is an unwanted import. The import is caused by code that the author knows can never be reached. This import can be removed by using a negating pattern. A pattern is inserted in the import as an extra import when it contains no wildcards and there is no referral to that package. This can be used to add an import statement for a package that is not referred to by your code but is still needed, for example, because the class is loaded by name.
For example:
+Import-Package: !org.apache.commons.log4j, com.acme.*,\
+ com.foo.extra
+During processing, bnd will attempt to find the exported version of imported packages. If no version or version range is specified on the import instruction, the exported version will then be used though the micro part and the qualifier are dropped. That is, when the exporter is 1.2.3.build123, then the import version will be 1.2. If a specific version (range) is specified, this will override any found version. This default an be overridden with the -consumerpolicy or -providerpolicy instruction (also see Versioning).
If an explicit version is given, then ${@} can be used to substitute the found version in a range. In those cases, the range macro can be very useful to calculate ranges and drop specific parts of the version. For example:
Import-Package: org.osgi.framework;version="[1.3,2.0)"
+Import-Package: org.osgi.framework;version="${@}"
+Import-Package: org.osgi.framework;version="${range;[==,=+);${@}}"
+You can reference the Bundle-SymbolicName and Bundle-Version of the exporter on the classpath by using the ${@bundlesymbolicname} and ${@bundleversion} values. In those cases, the range macro can be very useful to calculate ranges and drop specific parts of the bundle version. For example:
Import-Package: org.eclipse.jdt.ui;bundle-symbolic-name="${@bundlesymbolicname}";\
+ bundle-version="${range;[==,+);${@bundleversion}}"
+Packages with directive resolution:=dynamic will be removed from Import-Package and added to the DynamicImport-Package header after being processed like any other Import-Package entry. For example:
Import-Package: org.slf4j.*;resolution:=dynamic, *
+If an imported package uses mandatory attributes, then bnd will attempt to add those attributes to the import statement. However, in certain (bizarre!) cases this is not wanted. It is therefore possible to remove an attribute from the import clause. This is done with the -remove-attribute directive or by setting the value of an attribute to !. The parameter of the -remove-attribute directive is an instruction and can use the standard options with !, *, ?, etc.
Import-Package: org.eclipse.core.runtime;-remove-attribute:="common",*
+Or
+Import-Package: org.eclipse.core.runtime;common=!,*
+Directives that are not part of the OSGi specification will give a warning unless they are prefixed with x-.
TODO Needs review - AI Generated content
+Example: javac.encoding: UTF-8
Pattern: .*
.*Example: javac.profile: compact1
Pattern: .*
Example: javac.source: 1.8
Pattern: .*
Example: javac.target: 1.8
Pattern: .*
Example: Meta-Persistence: persistence/myPu.xml
Pattern: .*
The Meta-Persistence header marks a bundle as a JPA (Java Persistence API) persistence bundle. It lists one or more resource paths (typically XML files) that describe the persistence units. If this header is not present, the bundle is not considered a persistence bundle and should be ignored by JPA providers.
Example:
+Meta-Persistence: META-INF/persistence.xml
+bnd will verify that the listed resources exist in the bundle. This header is required for JPA persistence bundles.
+/**
+ * Verify the Meta-Persistence header
+ *
+ * @throws Exception
+ */
+
+public void verifyMetaPersistence() throws Exception {
+ List<String> list = new ArrayList<String>();
+ String mp = dot.getManifest().getMainAttributes().getValue(META_PERSISTENCE);
+ for (String location : OSGiHeader.parseHeader(mp).keySet()) {
+ String[] parts = location.split("!/");
+
+ Resource resource = dot.getResource(parts[0]);
+ if (resource == null)
+ list.add(location);
+ else {
+ if (parts.length > 1) {
+ Jar jar = new Jar("", resource.openInputStream());
+ try {
+ resource = jar.getResource(parts[1]);
+ if (resource == null)
+ list.add(location);
+ }
+ catch (Exception e) {
+ list.add(location);
+ }
+ finally {
+ jar.close();
+ }
+ }
+ }
+ }
+ if (list.isEmpty())
+ return;
+
+ error(Constants.META_PERSISTENCE + " refers to resources not in the bundle: %s", list).header(Constants.META_PERSISTENCE);
+}
+TODO Needs review - AI Generated content
+Example: Private-Package: com.*
Values: ${packages}
Pattern: .*
The Private-Package header specifies which packages should be included in the bundle but not exported. The syntax and behavior are similar to the Export-Package header, but these packages are private to the bundle and not visible to other bundles.
Example:
+Private-Package: com.example.internal.*
+If a package is selected by both the export and private package headers, the export takes precedence. bnd will merge split packages by default, but you can control this with the -split-package: directive.
The method of inclusion is identical to the Export-Package header, the only difference is, is that these packages are not exported. This header will be copied to the manifest. If a package is selected by the export and private package headers, then the export takes precedence.
+Private-Package= com.*
+Bnd traverse the packages on the classpath and copies them to the output based on the instructions given by the Export-Package and Private-Package headers. This opens up for the possibility that there are multiple packages with the same name on the class path. It is better to avoid this situation because it means there is no cohesive definition of the package and it is just, eh, messy. However, there are valid cases that packages should be merged from different sources. For example, when a standard package needs to be merged with implementation code like the osgi packages sometimes (unfortunately) do. Without any extra instructions, bnd will merge multiple packages where the last one wins if the packages contain duplicate resources, but it will give a warning to notify the unwanted case of split packages.
+The -split-package: directive on the Export-Package/Private-Package clause allows fine grained control over what should be done with split packages. The following values are architected:
||merge-firstmerge-lastfirst||Do not merge, only use the first package found||
+||error||Generate an error when a split package is detected||
For example:
+Private-Package: test.pack;-split-package:=merge-first
+private void doExpand(Jar dot) {
+
+ // Build an index of the class path that we can then
+ // use destructively
+ MultiMap<String,Jar> packages = new MultiMap<String,Jar>();
+ for (Jar srce : getClasspath()) {
+ dot.updateModified(srce.lastModified, srce + " (" + srce.lastModifiedReason + ")");
+ for (Entry<String,Map<String,Resource>> e : srce.getDirectories().entrySet()) {
+ if (e.getValue() != null)
+ packages.add(e.getKey(), srce);
+ }
+ }
+
+ Parameters privatePackages = getPrivatePackage();
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ String h = getProperty(Constants.TESTPACKAGES, "test;presence:=optional");
+ privatePackages.putAll(parseHeader(h));
+ }
+
+ if (!privatePackages.isEmpty()) {
+ Instructions privateFilter = new Instructions(privatePackages);
+ Set<Instruction> unused = doExpand(dot, packages, privateFilter);
+
+ if (!unused.isEmpty()) {
+ warning("Unused " + Constants.PRIVATE_PACKAGE + " instructions, no such package(s) on the class path: %s", unused);
+ }
+ }
+
+ Parameters exportedPackage = getExportPackage();
+ if (!exportedPackage.isEmpty()) {
+ Instructions exportedFilter = new Instructions(exportedPackage);
+
+ // We ignore unused instructions for exports, they should show
+ // up as errors during analysis. Otherwise any overlapping
+ // packages with the private packages should show up as
+ // unused
+
+ doExpand(dot, packages, exportedFilter);
+ }
+}
+
+
+
+
+
+
+ /**
+ * Allow any local initialization by subclasses before we build.
+ */
+public void init() throws Exception {
+ begin();
+ doRequireBnd();
+
+ // Check if we have sensible setup
+
+ if (getClasspath().size() == 0
+ && (getProperty(EXPORT_PACKAGE) != null || getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null || getProperty(PRIVATEPACKAGE) != null))
+ warning("Classpath is empty. " + Constants.PRIVATE_PACKAGE + " (-privatepackage) and " + EXPORT_PACKAGE + " can only expand from the classpath when there is one");
+
+}
+
+
+
+ /**
+ * Check if the given resource is in scope of this bundle. That is, it
+ * checks if the Include-Resource includes this resource or if it is a class
+ * file it is on the class path and the Export-Package or Private-Package
+ * include this resource.
+ *
+ * @param f
+ * @return
+ */
+public boolean isInScope(Collection<File> resources) throws Exception {
+ Parameters clauses = parseHeader(getProperty(Constants.EXPORT_PACKAGE));
+ clauses.putAll(parseHeader(getProperty(Constants.PRIVATE_PACKAGE)));
+ clauses.putAll(parseHeader(getProperty(Constants.PRIVATEPACKAGE)));
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ clauses.putAll(parseHeader(getProperty(Constants.TESTPACKAGES, "test;presence:=optional")));
+ }
+
+ Collection<String> ir = getIncludedResourcePrefixes();
+
+ Instructions instructions = new Instructions(clauses);
+
+ for (File r : resources) {
+ String cpEntry = getClasspathEntrySuffix(r);
+
+ if (cpEntry != null) {
+
+ if (cpEntry.equals("")) // Meaning we actually have a CPE
+ return true;
+
+ String pack = Descriptors.getPackage(cpEntry);
+ Instruction i = matches(instructions, pack, null, r.getName());
+ if (i != null)
+ return !i.isNegated();
+ }
+
+ // Check if this resource starts with one of the I-C header
+ // paths.
+ String path = r.getAbsolutePath();
+ for (String p : ir) {
+ if (path.startsWith(p))
+ return true;
+ }
+ }
+ return false;
+}
+TODO Needs review - AI Generated content
+Example: Provide-Capability: com.acme.dictionary; from:String=nl; to=de; version:Version=3.4
Pattern: .*
effective: (resolve) Specifies the time a capabiltity is available, either resolve (default) or another name. The OSGi framework resolver only considers Capabilities without an effective directive or effective:=resolve. Capabilties with other values for the effective directive can be considered by an external agent.Example: effective:=resolve
Values: resolve or another word
Pattern: .*
uses: The uses directive lists package names that are used by this Capability. This information is intended to be used for uses constraints.
Example: uses:='foo,bar,baz'
Pattern: .*
The Provide-Capability header declares that a bundle provides one or more OSGi capabilities. Capabilities are used by the OSGi framework to match requirements from other bundles. Each capability can include a namespace, attributes, and directives.
Example:
+Provide-Capability: osgi.service;objectClass=com.example.MyService;version=1.0.0
+This header is used for advanced modularity and service scenarios, and is typically generated by annotations or build tooling.
+ Parameters capabilities = new Parameters(annotationHeaders.getHeader(PROVIDE_CAPABILITY));
+
+
+ /*
+ * Provide-Capability header
+ */
+private void doProvideCapability(ProvideCapability annotation) {
+ StringBuilder sb = new StringBuilder(annotation.ns());
+ if (annotation.name() != null)
+ sb.append(";").append(annotation.ns()).append("='").append(annotation.name()).append("'");
+ if (annotation.uses() != null)
+ sb.append(";").append("uses:='").append(Strings.join(",", annotation.uses())).append("'");
+ if (annotation.mandatory() != null)
+ sb.append(";").append("mandatory:='").append(Strings.join(",", annotation.mandatory())).append("'");
+ if (annotation.version() != null)
+ sb.append(";").append("version:Version='").append(annotation.version()).append("'");
+ if (annotation.value() != null)
+ sb.append(";").append(annotation.value());
+ if (annotation.effective() != null)
+ sb.append(";effective:='").append(annotation.effective()).append("'");
+
+ add(Constants.PROVIDE_CAPABILITY, sb.toString());
+}
+
+
+
+
+ package aQute.bnd.annotation.headers;
+
+ import java.lang.annotation.*;
+
+ /**
+ * Define a Provide Capability clause in the manifest.
+ * <p>
+ * Since this annotation can only be applied once, it is possible to create an annotation
+ * that models a specific capability. For example:
+ * <pre>
+ * interface Webserver {
+ * @ProvideCapability(ns="osgi.extender", name="aQute.webserver", version = "${@version}")
+ * @interface Provide {}
+ *
+ * @RequireCapability(ns="osgi.extender", filter="(&(osgi.extender=aQute.webserver)${frange;${@version}})")
+ * @interface Require {}
+ * }
+ *
+ * Webserver.@Provide
+ * public class MyWebserver {
+ * }
+ * </pre>
+ *
+ */
+ @Retention(RetentionPolicy.CLASS)
+ @Target({
+ ElementType.ANNOTATION_TYPE, ElementType.TYPE
+ })
+ public @interface ProvideCapability {
+ /**
+ * Appended at the end of the clause (after a ';'). Can be used to add
+ * additional attributes and directives.
+ */
+ String value() default "";
+
+ /**
+ * The capability namespace. For example: {@code osgi.contract}.
+ */
+ String ns();
+
+ /**
+ * The name of the capability. If this is set, a property will be added as
+ * {ns}={name}. This is the custom pattern for OSGi namespaces. Leaving this
+ * unfilled, requires the {@link #value()} to be used to specify the name of
+ * the capability, if needed. For example {@code aQute.sse}.
+ */
+ String name() default "";
+
+ /**
+ * The version of the capability. This must be a valid OSGi version.
+ */
+ String version() default "";
+
+ /**
+ * Effective time. Specifies the time a capability is available, either
+ * resolve (default) or another name. The OSGi framework resolver only
+ * considers Capabilities without an effective directive or
+ * effective:=resolve. Capabilities with other values for the effective
+ * directive can be considered by an external agent.
+ */
+ String effective() default "resolve";
+
+ /**
+ * The uses directive lists package names that are used by this Capability.
+ * This information is intended to be used for <em>uses constraints</em>,
+ */
+ String[] uses() default {};
+
+ /**
+ * Mandatory attributes. Forces the resolver to only satisfy filters that
+ * refer to all listed attributes.
+ */
+ String[] mandatory() default {};
+ }
+
+
+
+ verifyDirectives(Constants.PROVIDE_CAPABILITY, "effective:|uses:", null, null);
+
+
+
+
+ private void verifyCapabilities() {
+ Parameters map = parseHeader(manifest.getMainAttributes().getValue(Constants.PROVIDE_CAPABILITY));
+ for (String key : map.keySet()) {
+ Attrs attrs = map.get(key);
+ verify(attrs, "cardinality:", CARDINALITY_PATTERN, false, "Requirement %s cardinality not correct", key);
+ verify(attrs, "resolution:", RESOLUTION_PATTERN, false, "Requirement %s resolution not correct", key);
+
+ if (key.equals("osgi.extender")) {
+ verify(attrs, "osgi.extender", SYMBOLICNAME, true,
+ "Extender %s must always have the osgi.extender attribute set", key);
+ verify(attrs, "version", VERSION, true, "Extender %s must always have a version", key);
+ } else if (key.equals("osgi.serviceloader")) {
+ verify(attrs, "register:", PACKAGEPATTERN, false,
+ "Service Loader extender register: directive not a fully qualified Java name");
+ } else if (key.equals("osgi.contract")) {
+ verify(attrs, "osgi.contract", SYMBOLICNAME, true,
+ "Contracts %s must always have the osgi.contract attribute set", key);
+
+ } else if (key.equals("osgi.service")) {
+ verify(attrs, "objectClass", MULTIPACKAGEPATTERN, true,
+ "osgi.service %s must have the objectClass attribute set", key);
+
+ } else if (key.equals("osgi.ee")) {
+ // TODO
+ } else if (key.startsWith("osgi.wiring.") || key.startsWith("osgi.identity")) {
+ error("osgi.wiring.* namespaces must not be specified with generic requirements/capabilities");
+ }
+
+ verifyAttrs(attrs);
+
+ if (attrs.containsKey("filter:"))
+ error("filter: directive is intended for Requirements, not Capability %s", key);
+ if (attrs.containsKey("cardinality:"))
+ error("cardinality: directive is intended for Requirements, not Capability %s", key);
+ if (attrs.containsKey("resolution:"))
+ error("resolution: directive is intended for Requirements, not Capability %s", key);
+ }
+}
+TODO Needs review - AI Generated content
+Example: Require-Bundle: com.acme.chess
Pattern: .*
visibility: If the value is private (Default), then all visible packages from the required bundles are not re-exported. If the value is reexport then bundles that require this bundle will transitively have access to these required bundle’s exported packages.Example: visibility:=private
Values: private,reexport
Pattern: private|reexport
resolution: If the value is mandatory (default) then the required bundle must exist for this bundle to resolve. If the value is optional, the bundle will resolve even if the required bundle does not exist.
Example: resolution:=optional
Values: mandatory,optional
Pattern: mandatory|optional
-split-package: Indicates how an imported package should be merged when it is split between different exporters. The default is merge-first with warning.
Example: -split-package:=merge-first
Values: merge-first,merge-last,error,first
Pattern: merge-first|merge-last|error|first
bundle-version A version range to select the bundle version of the exporting bundle. The default value is 0.0.0.
Example: bundle-version=1.3
Pattern: ((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The Require-Bundle header specifies that the bundle requires all exported packages from another bundle. This effectively imports the public interface of the required bundle, making its packages available to the requiring bundle.
Example:
+Require-Bundle: com.example.otherbundle
+This header is less common in OSGi and can make dependency management more complex. Prefer using Import-Package when possible.
verifyDirectives(Constants.REQUIRE_BUNDLE, "visibility:|resolution:", SYMBOLICNAME, "bsn");
+
+
+
+ //
+ // If there is a Require bundle, all bets are off and
+ // we cannot verify anything
+ //
+
+ if (domain.getRequireBundle().isEmpty() && domain.get("ExtensionBundle-Activator") == null
+ && (domain.getFragmentHost()== null || domain.getFragmentHost().getKey().equals("system.bundle"))) {
+
+ if (!unresolvedReferences.isEmpty()) {
+ // Now we want to know the
+ // classes that are the culprits
+ Set<String> culprits = new HashSet<String>();
+ for (Clazz clazz : analyzer.getClassspace().values()) {
+ if (hasOverlap(unresolvedReferences, clazz.getReferred()))
+ culprits.add(clazz.getAbsolutePath());
+ }
+
+ if (analyzer instanceof Builder)
+ warning("Unresolved references to %s by class(es) %s on the " + Constants.BUNDLE_CLASSPATH + ": %s",
+ unresolvedReferences, culprits, analyzer.getBundleClasspath().keySet());
+ else
+ error("Unresolved references to %s by class(es) %s on the " + Constants.BUNDLE_CLASSPATH + ": %s",
+ unresolvedReferences, culprits, analyzer.getBundleClasspath().keySet());
+ return;
+ }
+ } else if (isPedantic())
+ warning("Use of " + Constants.REQUIRE_BUNDLE + ", ExtensionBundle-Activator, or a system bundle fragment makes it impossible to verify unresolved references");
+TODO Needs review - AI Generated content
+Example: Require-Capability: com.microsoft; filter:='(&(api=win32)(version=7))'
Pattern: .*
effective: (resolve) Specifies the time a Requirement is considered, either resolve (default) or another name. The OSGi framework resolver only considers Requirements without an effective directive or effective:=resolve. Other Requirements can be considered by an external agent. Additonal names for the effective directive should be registered with the OSGi Alliance.Example: effective:=resolve
Values: resolve or another word
Pattern: .*
resolution: (mandatory|optional) A mandatory Requirement forbids the bundle to resolve when the Requirement is not satisfied; an optional Requirement allows a bundle to resolve even if the Requirement is not satisfied. No wirings are created when this Requirement cannot be resolved, this can result in Class Not Found Exceptions when the bundle attempts to use a package that was not resolved because it was optional.
Example: resolution:=optional
Values: mandatory,optional
Pattern: mandatory|optional
filter: (Filter) A filter expression that is asserted on the Capabilities belonging to the given namespace. The matching of the filter against the Capability is done on one Capability at a time. A filter like (&(a=1)(b=2)) matches only a Capability that specifies both attributes at the required value, not two capabilties that each specify one of the attributes correctly. A filter is optional, if no filter directive is specified the Requirement always matches.
Example: filter:= (&(a=1)(b=2))
Pattern: .*
The Require-Capability header declares that a bundle requires one or more OSGi capabilities to be present in the runtime environment. Each requirement can specify a namespace, filter, and other attributes or directives.
Example:
+Require-Capability: osgi.extender;filter:='(osgi.extender=osgi.component)'
+This header is used for advanced modularity and service scenarios, and is typically generated by annotations or build tooling.
+ Parameters requirements = new Parameters(annotationHeaders.getHeader(REQUIRE_CAPABILITY));
+ Parameters capabilities = new Parameters(annotationHeaders.getHeader(PROVIDE_CAPABILITY));
+
+ //
+ // Do any contracts contracts
+ //
+ contracts.addToRequirements(requirements);
+
+ //
+ // We want to add the minimum EE as a requirement
+ // based on the class version
+ //
+
+ if (!isTrue(getProperty(NOEE)) //
+ && !ees.isEmpty() // no use otherwise
+ && since(About._2_3) // we want people to not have to
+ // automatically add it
+ && !requirements.containsKey(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE) // and
+ // it
+ // should
+ // not
+ // be
+ // there
+ // already
+ ) {
+
+ JAVA highest = ees.last();
+ Attrs attrs = new Attrs();
+
+ String filter = doEEProfiles(highest);
+
+ attrs.put(Constants.FILTER_DIRECTIVE, filter);
+
+ //
+ // Java 1.8 introduced profiles.
+ // If -eeprofile= auto | (<profile>="...")+ is set then
+ // we add a
+
+ requirements.add(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE, attrs);
+ }
+
+ if (!requirements.isEmpty())
+ main.putValue(REQUIRE_CAPABILITY, requirements.toString());
+
+
+ /*
+ * Require-Capability header
+ */
+private void doRequireCapability(RequireCapability annotation) {
+ StringBuilder sb = new StringBuilder(annotation.ns());
+ if (annotation.filter() != null)
+ sb.append(";filter:='").append(annotation.filter()).append("'");
+ if (annotation.effective() != null)
+ sb.append(";effective:='").append(annotation.effective()).append("'");
+ if (annotation.resolution() != null)
+ sb.append(";resolution:='").append(annotation.resolution()).append("'");
+
+ if (annotation.value() != null)
+ sb.append(";").append(annotation.value());
+
+ add(Constants.REQUIRE_CAPABILITY, sb.toString());
+}
+
+
+package aQute.bnd.annotation.headers;
+
+ import java.lang.annotation.*;
+
+ /**
+ * The Bundle’s Require-Capability header
+ *
+ * {@link About}
+ */
+ @Retention(RetentionPolicy.CLASS)
+ @Target({
+ ElementType.ANNOTATION_TYPE, ElementType.TYPE
+ })
+ public @interface RequireCapability {
+ String value() default "";
+
+ /**
+ * The capability namespace. For example: {@code osgi.contract}.
+ */
+ String ns();
+
+ /**
+ * Specifies the time a Requirement is considered, either 'resolve'
+ * (default) or another name. The OSGi framework resolver only considers
+ * Requirements without an effective directive or effective:=resolve. Other
+ * Requirements can be considered by an external agent. Additional names for
+ * the effective directive should be registered with the OSGi Alliance. See
+ * <a href="https://www.osgi.org/developer/specifications/reference/">OSGi Reference
+ * Page</a>
+ */
+ String effective() default "resolve";
+
+ /**
+ * A filter expression that is asserted on the Capabilities belonging to the
+ * given namespace. The matching of the filter against the Capability is
+ * done on one Capability at a time. A filter like {@code (&(a=1)(b=2))}
+ * matches only a Capability that specifies both attributes at the required
+ * value, not two capabilties that each specify one of the attributes
+ * correctly. A filter is optional, if no filter directive is specified the
+ * Requirement always matches.
+ */
+ String filter();
+
+ /**
+ * A mandatory Requirement forbids the bundle to resolve when the
+ * Requirement is not satisfied; an optional Requirement allows a bundle to
+ * resolve even if the Requirement is not satisfied. No wirings are created
+ * when this Requirement cannot be resolved, this can result in Class Not
+ * Found Exceptions when the bundle attempts to use a package that was not
+ * resolved because it was optional.
+ */
+ Resolution resolution() default Resolution.mandatory;
+
+ }
+
+
+ private void verifyRequirements() {
+ Parameters map = parseHeader(manifest.getMainAttributes().getValue(Constants.REQUIRE_CAPABILITY));
+ for (String key : map.keySet()) {
+ Attrs attrs = map.get(key);
+ verify(attrs, "filter:", FILTERPATTERN, false, "Requirement %s filter not correct", key);
+
+ String filter = attrs.get("filter:");
+ if (filter != null) {
+ String verify = new Filter(filter).verify();
+ if (verify != null)
+ error("Invalid filter syntax in requirement %s=%s. Reason %s", key, attrs, verify);
+ }
+ verify(attrs, "cardinality:", CARDINALITY_PATTERN, false, "Requirement %s cardinality not correct", key);
+ verify(attrs, "resolution:", RESOLUTION_PATTERN, false, "Requirement %s resolution not correct", key);
+
+ if (key.equals("osgi.extender")) {
+ // No requirements on extender
+ } else if (key.equals("osgi.serviceloader")) {
+ verify(attrs, "register:", PACKAGEPATTERN, false,
+ "Service Loader extender register: directive not a fully qualified Java name");
+ } else if (key.equals("osgi.contract")) {
+
+ } else if (key.equals("osgi.service")) {
+
+ } else if (key.equals("osgi.ee")) {
+
+ } else if (key.startsWith("osgi.wiring.") || key.startsWith("osgi.identity")) {
+ error("osgi.wiring.* namespaces must not be specified with generic requirements/capabilities");
+ }
+
+ verifyAttrs(attrs);
+
+ if (attrs.containsKey("mandatory:"))
+ error("mandatory: directive is intended for Capabilities, not Requirement %s", key);
+
+ if (attrs.containsKey("uses:"))
+ error("uses: directive is intended for Capabilities, not Requirement %s", key);
+ }
+}
+TODO Needs review - AI Generated content
+Example: Service-Component=com.acme.Foo?;activate='start'
Pattern: .*
The Service-Component header lists XML files that describe Declarative Services (DS) components in the bundle. These XML documents must be present in the bundle and are used by the OSGi framework to register and manage components at runtime.
Example:
+Service-Component: OSGI-INF/components.xml
+This header is required for bundles that use OSGi Declarative Services.
+/**
+ * Analyze the class space for any classes that have an OSGi annotation for DS.
+ */
+public class DSAnnotations implements AnalyzerPlugin {
+
+ public boolean analyzeJar(Analyzer analyzer) throws Exception {
+ Parameters header = OSGiHeader.parseHeader(analyzer.getProperty(Constants.DSANNOTATIONS));
+ if (header.size() == 0)
+ return false;
+
+ Instructions instructions = new Instructions(header);
+ Collection<Clazz> list = analyzer.getClassspace().values();
+ String sc = analyzer.getProperty(Constants.SERVICE_COMPONENT);
+ List<String> names = new ArrayList<String>();
+ if (sc != null && sc.trim().length() > 0)
+ names.add(sc);
+
+ for (Clazz c: list) {
+ for (Instruction instruction : instructions.keySet()) {
+
+ if (instruction.matches(c.getFQN())) {
+ if (instruction.isNegated())
+ break;
+ ComponentDef definition = AnnotationReader.getDefinition(c, analyzer);
+ if (definition != null) {
+ definition.sortReferences();
+ definition.prepare(analyzer);
+ String name = "OSGI-INF/" + definition.name + ".xml";
+ names.add(name);
+ analyzer.getJar().putResource(name, new TagResource(definition.getTag()));
+ }
+ }
+ }
+ }
+ sc = Processor.append(names.toArray(new String[names.size()]));
+ analyzer.setProperty(Constants.SERVICE_COMPONENT, sc);
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "DSAnnotations";
+ }
+}
+
+ package aQute.bnd.make.component;
+
+ import java.io.*;
+ import java.util.*;
+ import java.util.Map.Entry;
+
+ import aQute.bnd.annotation.component.*;
+ import aQute.bnd.component.*;
+ import aQute.bnd.header.*;
+ import aQute.bnd.make.metatype.*;
+ import aQute.bnd.osgi.*;
+ import aQute.bnd.osgi.Clazz.QUERY;
+ import aQute.bnd.osgi.Descriptors.TypeRef;
+ import aQute.bnd.service.*;
+ import aQute.lib.tag.*;
+
+ /**
+ * This class is an analyzer plugin. It looks at the properties and tries to
+ * find out if the Service-Component header contains the bnd shortut syntax. If
+ * not, the header is copied to the output, if it does, an XML file is created
+ * and added to the JAR and the header is modified appropriately.
+ */
+ public class ServiceComponent implements AnalyzerPlugin {
+
+ public boolean analyzeJar(Analyzer analyzer) throws Exception {
+
+ ComponentMaker m = new ComponentMaker(analyzer);
+
+ Map<String,Map<String,String>> l = m.doServiceComponent();
+
+ analyzer.setProperty(Constants.SERVICE_COMPONENT, Processor.printClauses(l));
+
+ analyzer.getInfo(m, Constants.SERVICE_COMPONENT + ": ");
+ m.close();
+
+ return false;
+ }
+
+ private static class ComponentMaker extends Processor {
+ Analyzer analyzer;
+
+ ComponentMaker(Analyzer analyzer) {
+ super(analyzer);
+ this.analyzer = analyzer;
+ }
+
+ /**
+ * Iterate over the Service Component entries. There are two cases:
+ * <ol>
+ * <li>An XML file reference</li>
+ * <li>A FQN/wildcard with a set of attributes</li>
+ * </ol>
+ * An XML reference is immediately expanded, an FQN/wildcard is more
+ * complicated and is delegated to
+ * {@link #componentEntry(Map, String, Map)}.
+ *
+ * @throws Exception
+ */
+ Map<String,Map<String,String>> doServiceComponent() throws Exception {
+ Map<String,Map<String,String>> serviceComponents = newMap();
+ String header = getProperty(SERVICE_COMPONENT);
+ Parameters sc = parseHeader(header);
+
+ for (Entry<String,Attrs> entry : sc.entrySet()) {
+ String name = entry.getKey();
+ Map<String,String> info = entry.getValue();
+
+ try {
+ if (name.indexOf('/') >= 0 || name.endsWith(".xml")) {
+ // Normal service component, we do not process it
+ serviceComponents.put(name, EMPTY);
+ } else {
+ componentEntry(serviceComponents, name, info);
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ error("Invalid " + Constants.SERVICE_COMPONENT + " header: %s %s, throws %s", name, info, e);
+ throw e;
+ }
+ }
+ return serviceComponents;
+ }
+
+ /**
+ * Parse an entry in the Service-Component header. This header supports
+ * the following types:
+ * <ol>
+ * <li>An FQN + attributes describing a component</li>
+ * <li>A wildcard expression for finding annotated components.</li>
+ * </ol>
+ * The problem is the distinction between an FQN and a wildcard because
+ * an FQN can also be used as a wildcard. If the info specifies
+ * {@link Constants#NOANNOTATIONS} then wildcards are an error and the
+ * component must be fully described by the info. Otherwise the
+ * FQN/wildcard is expanded into a list of classes with annotations. If
+ * this list is empty, the FQN case is interpreted as a complete
+ * component definition. For the wildcard case, it is checked if any
+ * matching classes for the wildcard have been compiled for a class file
+ * format that does not support annotations, this can be a problem with
+ * JSR14 who silently ignores annotations. An error is reported in such
+ * a case.
+ *
+ * @param serviceComponents
+ * @param name
+ * @param info
+ * @throws Exception
+ * @throws IOException
+ */
+ private void componentEntry(Map<String,Map<String,String>> serviceComponents, String name,
+ Map<String,String> info) throws Exception, IOException {
+
+ boolean annotations = !Processor.isTrue(info.get(NOANNOTATIONS));
+ boolean fqn = Verifier.isFQN(name);
+
+ if (annotations) {
+
+ // Annotations possible!
+
+ Collection<Clazz> annotatedComponents = analyzer.getClasses("", QUERY.ANNOTATED.toString(),
+ Component.class.getName(), //
+ QUERY.NAMED.toString(), name //
+ );
+
+ if (fqn) {
+ if (annotatedComponents.isEmpty()) {
+
+ // No annotations, fully specified in header
+
+ createComponentResource(serviceComponents, name, info);
+ } else {
+
+ // We had a FQN so expect only one
+
+ for (Clazz c : annotatedComponents) {
+ annotated(serviceComponents, c, info);
+ }
+ }
+ } else {
+
+ // We did not have an FQN, so expect the use of wildcards
+
+ if (annotatedComponents.isEmpty())
+ checkAnnotationsFeasible(name);
+ else
+ for (Clazz c : annotatedComponents) {
+ annotated(serviceComponents, c, info);
+ }
+ }
+ } else {
+ // No annotations
+ if (fqn)
+ createComponentResource(serviceComponents, name, info);
+ else
+ error("Set to %s but entry %s is not an FQN ", NOANNOTATIONS, name);
+
+ }
+ }
+
+ /**
+ * Check if annotations are actually feasible looking at the class
+ * format. If the class format does not provide annotations then it is
+ * no use specifying annotated components.
+ *
+ * @param name
+ * @return
+ * @throws Exception
+ */
+ private Collection<Clazz> checkAnnotationsFeasible(String name) throws Exception {
+ Collection<Clazz> not = analyzer.getClasses("", QUERY.NAMED.toString(), name //
+ );
+
+ if (not.isEmpty()) {
+ if ("*".equals(name))
+ return not;
+ error("Specified %s but could not find any class matching this pattern", name);
+ }
+
+ for (Clazz c : not) {
+ if (c.getFormat().hasAnnotations())
+ return not;
+ }
+
+ warning("Wildcards are used (%s) requiring annotations to decide what is a component. Wildcard maps to classes that are compiled with java.target < 1.5. Annotations were introduced in Java 1.5",
+ name);
+
+ return not;
+ }
+
+ void annotated(Map<String,Map<String,String>> components, Clazz c, Map<String,String> info) throws Exception {
+ // Get the component definition
+ // from the annotations
+ Map<String,String> map = ComponentAnnotationReader.getDefinition(c, this);
+
+ // Pick the name, the annotation can override
+ // the name.
+ String localname = map.get(COMPONENT_NAME);
+ if (localname == null)
+ localname = c.getFQN();
+
+ // Override the component info without manifest
+ // entries. We merge the properties though.
+
+ String merged = Processor.merge(info.remove(COMPONENT_PROPERTIES), map.remove(COMPONENT_PROPERTIES));
+ if (merged != null && merged.length() > 0)
+ map.put(COMPONENT_PROPERTIES, merged);
+ map.putAll(info);
+ createComponentResource(components, localname, map);
+ }
+
+ private void createComponentResource(Map<String,Map<String,String>> components, String name,
+ Map<String,String> info) throws Exception {
+
+ // We can override the name in the parameters
+ if (info.containsKey(COMPONENT_NAME))
+ name = info.get(COMPONENT_NAME);
+
+ // Assume the impl==name, but allow override
+ String impl = name;
+ if (info.containsKey(COMPONENT_IMPLEMENTATION))
+ impl = info.get(COMPONENT_IMPLEMENTATION);
+
+ TypeRef implRef = analyzer.getTypeRefFromFQN(impl);
+ // Check if such a class exists
+ analyzer.referTo(implRef);
+
+ boolean designate = designate(name, info.get(COMPONENT_DESIGNATE), false)
+ || designate(name, info.get(COMPONENT_DESIGNATEFACTORY), true);
+
+ // If we had a designate, we want a default configuration policy of
+ // require.
+ if (designate && info.get(COMPONENT_CONFIGURATION_POLICY) == null)
+ info.put(COMPONENT_CONFIGURATION_POLICY, "require");
+
+ // We have a definition, so make an XML resources
+ Resource resource = createComponentResource(name, impl, info);
+ analyzer.getJar().putResource("OSGI-INF/" + name + ".xml", resource);
+
+ components.put("OSGI-INF/" + name + ".xml", EMPTY);
+
+ }
+
+ /**
+ * Create a Metatype and Designate record out of the given
+ * configurations.
+ *
+ * @param name
+ * @param config
+ * @throws Exception
+ */
+ private boolean designate(String name, String config, boolean factory) throws Exception {
+ if (config == null)
+ return false;
+
+ for (String c : Processor.split(config)) {
+ TypeRef ref = analyzer.getTypeRefFromFQN(c);
+ Clazz clazz = analyzer.findClass(ref);
+ if (clazz != null) {
+ analyzer.referTo(ref);
+ MetaTypeReader r = new MetaTypeReader(clazz, analyzer);
+ r.setDesignate(name, factory);
+ String rname = "OSGI-INF/metatype/" + name + ".xml";
+
+ analyzer.getJar().putResource(rname, r);
+ } else {
+ analyzer.error("Cannot find designated configuration class %s for component %s", c, name);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Create the resource for a DS component.
+ *
+ * @param list
+ * @param name
+ * @param info
+ * @throws UnsupportedEncodingException
+ */
+ Resource createComponentResource(String name, String impl, Map<String, String> info)
+ throws Exception {
+ HeaderReader hr = new HeaderReader(analyzer);
+ Tag tag = hr.createComponentTag(name, impl, info);
+ hr.close();
+ return new TagResource(tag);
+ }
+ }
+
+ }
+
+
+
+
+
+
+ private void verifyComponent() {
+ String serviceComponent = main.get(Constants.SERVICE_COMPONENT);
+ if (serviceComponent != null) {
+ Parameters map = parseHeader(serviceComponent);
+ for (String component : map.keySet()) {
+ if (component.indexOf("*") < 0 && !dot.exists(component)) {
+ error(Constants.SERVICE_COMPONENT + " entry can not be located in JAR: " + component);
+ } else {
+ // validate component ...
+ }
+ }
+ }
+}
+TODO Needs review - AI Generated content
+Example: src: src/main/java, src/main/resources
Pattern: .*
Example: target-dir: target
Pattern: .*
The target-dir header is also useful to avoid conflicts between different build tools.
Example: Test-Cases: com.foo.bar.MyTest
Pattern: .*
The Test-Cases header specifies one or more test classes to be automatically executed by the bnd JUnit tester. This header is used to configure which tests should run when the bundle is tested in an OSGi environment.
Example:
+Test-Cases: com.example.MyTest, com.example.AnotherTest
+This header is typically set by build tooling or annotations and is required for automatic test execution with bnd's JUnit integration.
+private void checkForTesting(Project project, Properties properties) throws Exception {
+
+ //
+ // Only run junit when we have a test src directory
+ //
+
+ boolean junit = project.getTestSrc().isDirectory() && !Processor.isTrue(project.getProperty(Constants.NOJUNIT));
+ boolean junitOsgi = project.getProperties().getProperty(Constants.TESTCASES) != null
+ && !Processor.isTrue(project.getProperty(Constants.NOJUNITOSGI));
+
+ if (junit)
+ properties.setProperty("project.junit", "true");
+ if (junitOsgi)
+ properties.setProperty("project.osgi.junit", "true");
+}
+
+public void test(List<String> tests) throws Exception {
+
+ String testcases = getProperties().getProperty(Constants.TESTCASES);
+ if (testcases == null) {
+ warning("No %s set", Constants.TESTCASES);
+ return;
+ }
+ clear();
+
+ ProjectTester tester = getProjectTester();
+ if ( tests != null) {
+ trace("Adding tests %s", tests);
+ for ( String test : tests) {
+ tester.addTest(test);
+ }
+ }
+ tester.setContinuous(isTrue(getProperty(Constants.TESTCONTINUOUS)));
+ tester.prepare();
+
+ if (!isOk()) {
+ return;
+ }
+ int errors = tester.test();
+ if (errors == 0) {
+ System.err.println("No Errors");
+ } else {
+ if (errors > 0) {
+ System.err.println(errors + " Error(s)");
+
+ } else
+ System.err.println("Error " + errors);
+ }
+}
+
+ void automatic() throws IOException {
+ String testerDir = context.getProperty(TESTER_DIR);
+ if (testerDir == null)
+ testerDir = "testdir";
+
+ final File reportDir = new File(testerDir);
+ final List<Bundle> queue = new Vector<Bundle>();
+ if (!reportDir.exists() && !reportDir.mkdirs()) {
+ throw new IOException("Could not create directory " + reportDir);
+ }
+ trace("using %s, needed creation %s", reportDir, reportDir.mkdirs());
+
+ trace("adding Bundle Listener for getting test bundle events");
+ context.addBundleListener(new SynchronousBundleListener() {
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.STARTED) {
+ checkBundle(queue, event.getBundle());
+ }
+
+ }
+ });
+
+ for (Bundle b : context.getBundles()) {
+ checkBundle(queue, b);
+ }
+
+ trace("starting queue");
+ int result = 0;
+ outer: while (active) {
+ Bundle bundle;
+ synchronized (queue) {
+ while (queue.isEmpty() && active) {
+ try {
+ queue.wait();
+ }
+ catch (InterruptedException e) {
+ trace("tests bundle queue interrupted");
+ thread.interrupt();
+ break outer;
+ }
+ }
+ }
+ try {
+ bundle = queue.remove(0);
+ trace("received bundle to test: %s", bundle.getLocation());
+ Writer report = getReportWriter(reportDir, bundle);
+ try {
+ trace("test will run");
+ result += test(bundle, (String) bundle.getHeaders().get(aQute.bnd.osgi.Constants.TESTCASES), report);
+ trace("test ran");
+ if (queue.isEmpty() && !continuous) {
+ trace("queue " + queue);
+ System.exit(result);
+ }
+ }
+ finally {
+ if (report != null)
+ report.close();
+ }
+ }
+ catch (Exception e) {
+ error("Not sure what happened anymore %s", e);
+ System.exit(254);
+ }
+ }
+}
+
+
+ void checkBundle(List<Bundle> queue, Bundle bundle) {
+ if (bundle.getState() == Bundle.ACTIVE) {
+ String testcases = (String) bundle.getHeaders().get(aQute.bnd.osgi.Constants.TESTCASES);
+ if (testcases != null) {
+ trace("found active bundle with test cases %s : %s", bundle, testcases);
+ synchronized (queue) {
+ queue.add(bundle);
+ queue.notifyAll();
+ }
+ }
+ }
+}
+
+
+
+
+ if (testcases == null) {
+// if ( !continuous) { +// System.err.println("\nThe -testcontinuous property must be set if invoked without arguments\n"); +// System.exit(255); +// }
+ trace("automatic testing of all bundles with " + aQute.bnd.osgi.Constants.TESTCASES + " header");
+ try {
+ automatic();
+ }
+ catch (IOException e) {
+ // ignore
+ }
+TODO Needs review - AI Generated content
+Example: testbin: target/test-classes
Pattern: .*
Example: Tester-Plugin= a.b.c.MyTester
Pattern: .*
Example: testsrc: src/test/java
Pattern: .*
.*The Tool header in the bundle manifest indicates the version of bnd that was used to build the bundle. This is automatically set by bnd during the build process and helps with tracking, debugging, and auditing builds.
The value is typically in the form:
+Tool: Bnd-7.0.0
+This header is set by bnd and should not be manually changed.
+TODO Needs review - AI Generated content
+bnd is the Swiss army knife of OSGi, used for creating and working with OSGi bundles.
+ +Full bnd documentation: https://bnd.bndtools.org
+Full Bndtools documentation: https://bndtools.org
Example: -augment: com.example.prime; capability:='osgi.extender; osgi.extender=osgi.component; version:Version=1.2'
Pattern: .*
capability: This directive specifies a Provide-Capability instruction, this will therefore likely have to be quoted. Any number of clauses can be specified.Example: capability:=osgi.extender; osgi.extender=osgi.component; version:Version=1.2
Pattern: .*
requirement: The directive specifies a Require-Capability instruction
Example: requirement:=osgi.identity;filter:="(osgi.identity=a.b.c)"
Pattern: .*
version: A version range. If a single version is given it will be used as [
.*The -augment instruction can be used to augment resources in the repositories. Augmenting is adding additional capabilities and requirements. When bnd resolves a project or bndrun file then, it will read this variable (it is a merge property so you can also use additional keys like -augment.xyz) and use it to decorate the repository entries.
The key of the PARAMETER is for the bundle symbolic name. It can contain the * wildcard character to match multiple bundles. The bundle symbolic name must be allowed as a value in a filter it is therefore not a globbing expression.
The following directives and attribute are architected:
+version – A version range. If a single version is given it will be used as [<version>,∞). The version range can be prefixed with an '@' for a consumer range (to the next major) or a provider range (to the next minor) when the '@' is a suffix of the version. The range can restrict the augmentation to a limited set of bundles. capability: – The capability: directive specifies a Provide-Capability instruction, this will therefore likely have to be quoted. Any number of clauses can be specified.requirement: – The requirement: directive specifies a Require-Capability instruction.To augment the repositories during a resolve, bnd will find all bundles that match the bundle symbolic name and fall within the defined range. If no range is given, only the bundle symbolic name will constrain the search. Each found bundle will then be decorated with the capabilities and requirements defined in the capability: and requirement: directive.
For example, we need to provide an extender capability to a bundle with the bundle symbolic name com.example.prime.
-augment.prime = \
+ com.example.prime; \
+ capability:='osgi.extender; \
+ osgi.extender=osgi.component; \
+ version:Version=1.2'
+The capability: and requirement: directives follow all the rules of the Provide-Capability and Require-Capability headers respectively. For the resolver, it is as if these headers were specified in their manifests. There is one exception, the cap: and req: directives also support capabilities from the osgi.wiring.* name spaces.
It should be realized that the augmentation is only used during resolving. These requirements & capabilities are not added to the actual bundles during runtime. The primary purpose is to ensure that the resolve can properly assemble a system; great care should be taken to not create assemblies that cannot be resolved by the framework.
+Example: -baseline: com.example.*
Pattern: .*
Baselining uses the previous revision of a project with a baseline bundle to detect any changes in semantic versioning using the rules of binary compatibility. The -baseline instruction enables baselining for one or more symbolic names, the instruction takes a selector as input. Each bundle that is being build is held against this selector and if it matches then it is baselined. The following example will baseline any bundle whose name starts with com.example..
-baseline: com.example.*
+By default a bundle's baseline is the revision with the highest version in the repositories. However, the baseline can also be set with a file or version attribute on the selector.
version – The target will be the bundle with the lowest version that is higher than the given version.file – The target will be the given bundle. The file is relative to the project directory.
-baseline com.example.foo;version=1.2, com.example.bar;file=foo-1.2.3.jar
+Detected violations of the semantic versioning are reported as errors.
+See baselining for more information.
+Example: -baselinerepo=Baseline
Pattern: .*
The default repository for baselining is the release repository. However, this instruction can be set in the workspace to define an alternate repository. The reference qname is the name of the repository.
-baselinerepo: Baseline
+See baselining for more information.
+Example: -bnd-driver:eclipse
Values: (ant | gradle|eclipsebndgradle_nativeintellijmavenosmorcsbt)
Pattern: .*
Example: -builderignore=${if;${driver;gradle};bin,bin_test,generated;build}
Pattern: .*
Specified paths must be relative to the project. Each path represents a directory in a project to be ignored by the builder when deciding if the bundles of the project need to be built. This is processed for workspace model builds by the Bndtools builder in Eclipse and the Bnd Gradle plugin.
+This can be useful when the workspace is configured to use different output folders for Bndtools in Eclipse and for Gradle. For example:
+bin: ${if;${driver;gradle};build/classes/java/main;bin}
+testbin: ${if;${driver;gradle};build/classes/java/test;bin_test}
+target-dir: ${if;${driver;gradle};build/libs;generated}
+When configuring the workspace to use different output folders for Bndtools in Eclipse and for Gradle, you should also use -builderignore to instruct the builder to ignore the other builder's output folders.
-builderignore: ${if;${driver;gradle};bin,bin_test,generated;build}
+Example: -buildpath=osgi;version=4.1
Values: ${repo;bsns}
Pattern: [-\w]+(:?\.[-\w]+)*
version Specifies the range in the repository, project or file.Example: version=project
Values: project,type
Pattern: project|type|((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The -buildpath instruction is the main mechanism to add build-time dependencies to a project. A dependency is either another project in the workspace, or a bundle in a repository. The -buildpath is only used during compile and build time; it is never used to run projects. Because -buildpath dependencies are only used compile time it's recommended to add bundles containing only APIs; you don't need bundles containing implementations.
An example of the -buildpath could be the following, where three dependencies are defined:
-buildpath: \
+ some.other.workspace.project;version=project,\
+ osgi.core;version=4.3.1,\
+ osgi.cmpn;version=4.3.1
+Example: -buildrepo=Local
Pattern: .*
The -buildrepo instruction allows you to automatically release JARs built by a project to one or more specified repositories. After building, if -buildrepo is set, the resulting JARs are placed into the named repositories, which must already exist in the workspace. You can also provide additional properties to control the release context for each repository.
The syntax is as follows:
+ -buildrepo ::= repo ( ',' repo )*
+ repo ::= NAME ( ';' NAME ('=' VALUE)? )*
+The instruction provides a name of a repository, the repository must exist in the workspace. Any properties added to the name are provided as properties in the release context and thus given to the repository.
+This feature was inspired by the Maven Bnd Repository. In the Maven development process, projects are installed in the local repository (usually ~/.m2/repository) so they can be shared with other Maven projects. Setting the -buildrepo to a Maven repository will allow a bnd workspace to participate in this process on equal footing. Every time the project is build, all its JARs are installed in the associated Maven repository.
For example:
+-plugin.maven \
+ aQute.bnd.repository.maven.provider.MavenBndRepository; \
+ name=Local
+
+-buildrepo: Local
+The install process is taking place in-line with the build process. It is therefore recommended to only use this for local (i.e. file system based) installs.
+TODO Needs review - AI Generated content
+Example: -buildtool \
+ gradle; version=7.3.0; \
+ bnd_version = 6.1.0
+ bnd_snapshot=https://bndtools.jfrog.io/bndtools/libs-snapshot-local
Pattern: .*
In many projects, the workspace carries the files to build it with an external tool. Gradle is the most +popular but it is also possible to use other tools. An advantage of the workspace is that there are +no specific setups required, the workspace contains all the information needed to build. For example, +the bnd command line can build it. Tools are generally used to provide additional tasks.
+The setup files of the build tool therefore tend to be dead weight. Dead weight, that frequently needs
+overlapping properties with the bnd workspace. Since the tools tend get updated frequently, there are
+many software engineering advantages in installing the build tool before the project is being built. It
+also makes it easier to maintain many different workspace, they no longer need to be updated when a
+new release becomes available. Only the workspace cnf/build.bnd needs to be maintained.
TBD: a github action that will run the bnd command is foreseen
+The -buildtool instruction provides the information for the bnd command line tool to install a
+build tool template from the net. It has the following attributes:
version – If the version is an OSGi version, the key must be a tool name, for example gradle. The url used
+ to download the template will then be constructed of a url to
+ https://github.com/bndtools/workspace.tool.${key}/archive/refs/tags/${version}.zipIf it is not a version, it can be:
+ * url – The key is interpreted as a URL to a template zip file.
+ * file – The key is interpreted to a file local to the root of the workspace
+* * – Any other attributes will be expanded in a properties file
The template zip must contain a tool.bnd file. This file describes how the template should be installed. This file
+can be anywhere in the zip hierarchy, its parent directory is the root. Only files from the root will be copied.
The tool.bnd file must contain a -tool instruction.
-tool \
+ .*;tool.bnd;skip=true, \
+ gradle.properties;macro=true,\
+ gradlew;exec=true, \
+ *
+This is a normal instructions. The keys are matched against the file names relative from the root. The first matching +instruction, provides the instructions how to process the file. The following attributes are available.
+skip=* : Skip these filesmacro=* : Process macros. The macros can use the workspace macros and any macros specified in the tool.bnd file.append=* : Append the extra attributes of the -buildtool instruction to the matching files. This will happen in the format of bnd/properties files.exec=* : Make the file executable for the ownerThe attributes must have a value, this value is ignored.
+We want to use Gradle 7.3 with bnd 6.1.0. We therefore add the following to the bnd workspace cnf/build file:
-buildtool \
+ gradle; version=7.3.0; \
+ bnd_version = 6.1.0
+ bnd_snapshot=https://bndtools.jfrog.io/bndtools/libs-snapshot-local
+To install the tool, you can execute the bnd command buildtool:
$ bnd buildtool --force
+This will download the sources at https://github.com/bndtools/workspace.tool.gradle/archive/refs/heads/7.3.0.zip. The zip file
+looks something like:
workspace.tool.gradle/
+ tool.bnd
+ gradle/
+ wrapper/
+ gradle-wrapper.jar
+ gradle-wrapper.properties
+ gradle.properties
+ settings.gradle
+ gradlew.bat
+ gradlew
+ .gitignore
+The tool.bnd looks like:
-tool \
+ .*;tool.bnd;skip=true, \
+ gradle.properties;macro=true,\
+ gradlew;exec=true, \
+ *
+It should be clear that the gradle.properties is expanded. After the files are copied, it looks like:
# Appended by tool manager at 2021-12-02T14:40:55.660843Z
+version = url
+bnd_version = 6.1.0
+bnd_snapshots = https://bndtools.jfrog.io/bndtools/libs-snapshot-local
+After the command has finished, the gradle command can be executed
+$ ./gradlew --parallel clean build
+Example: -bumppolicy==+0
Values: ==+,=+0,+00
Pattern: [=+-0][=+-0][=+-0]
The bump command increases the versions in a bnd project. By default, it will use a compatible policy. A compatible to increment the minor part of the version and reset the micro part. The major part will remain the same. Using the versionmask syntax this looks like:
=+0
+The -bumppolicy macro can override this default. For example, the following instruction sets the policy to make only major increments.
+00
+Example: -bundleannotations: com.foo.bar.MyClazz
Pattern: .*
The -bundleannotations instruction tells bnd which bundle classes, if any, to search for OSGi Bundle annotations. bnd will then process those annotations into manifest headers.
The value of this instruction is a comma delimited list of fully qualified class names.
+The default value of this instruction is *, which means that by default bnd will process all bundle classes looking for Bundle annotations.
Example: -cdiannotations: *;discover=all
Pattern: .*
discover Bean Discovery Mode.Example: discover=all
Values: (all|annotated|annotated_by_bean|none)
Pattern: .*
noservicecapabilities indicates that no service capabilities will be added for matches.
Example: noservicecapabilities=true
Values: true, false
Pattern: true|false
The -cdiannotations instruction tells bnd which bundle classes, if any, to search for OSGI CDI Integration (or plain CDI) annotations. bnd will then process those classes into requirements and capabilities.
The value of this instruction is a comma delimited list of globs matching packages or classes by fully qualified name.
+The default value of this instruction is *, which means that by default bnd will process all bundle classes looking for OSGI CDI Integration (or plain CDI) annotations.
Each glob may specify the discover attribute which determines the bean discovery mode to apply to matches.
The following discover modes are supported:
discover |
+Bean Discovery Mode | +
|---|---|
all |
+include all classes in the bundle as CDI beans | +
annotated |
+include classes annotated with bean defining annotations as CDI beans | +
annotated_by_bean (default) |
+include classes annotated with org.osgi.service.cdi.annotations.Bean or classes in packages annotated with org.osgi.service.cdi.annotations.Beans as CDI beans. This is the default mode when discover is not specified. |
+
none |
+do not include any classes in the bundle as CDI beans | +
-cdiannotations: *;discover=all
+Each glob may specify the noservicecapabilities attribute which indicates that no service capabilities will be added for matches.
-cdiannotations: *;discover=annotated;noservicecapabilities=true
+Each glob may specify the noservicerequirements attribute which indicates that no service requirements will be added for matches.
-cdiannotations: *;noservicerequirements=true
+Example: -check=EXPORTS
Values: (ALL|EXPORTS|IMPORTS)
Pattern: .*
Adding extra checks to bnd will break existing builds and some people get a tad upset about that. However, some checks are actually really valuable. So this instruction is a contract. New checks that can break build will add additional enums to this list. So in theory builds should not be broken. Currently we have the following values defined
+ALL – Enable all checking, including checks that are added in the future. So for ALL, one day we might break your build. Your choice. Then again, this is the wisest choice since the checks we do are really useful to know if they are not satisfied.EXPORTS – Enable checking for exports. This checks if an exported package has contents.IMPORTS – Enable checking of imports. If an import is not exported by any bundle on the -buildpath then we have a bit of a problem. Trying to resolve the current bundle on a framework will almost certainly fail. If you really need an import but have no export then you can get rid of the warning by explicitly importing it in Import-Package. For example:
Import-Package: i.do.no.exist;version=@1.0.0, *
+Example: -baseline: jar/foo.jar, jar/bar.jar
Pattern: .*
The -classpath instruction adds class path entries to a bnd file's processing. It contains direct references to JARs through file paths or URLs. Entries on the class path are made available as imports and can be used as private or exported packages.
-classpath: jar/foo.jar, jar/bar.jar
+If you need to get all JARs in a directory you could use the ${lsa} macro:
-classpath: ${lsa;jar/*}
+This instruction should only be used when bnd is used in stand alone mode. In project mode (when you have a workspace), the -buildpath defines the actual class path. However, even in project mode any -classpath entries are added to the actual class path.
Example: -compression: STORE
Values: DEFLATE,STORE
Pattern: .*
DEFLATEValues: DEFLATE
Pattern: \QDEFLATE\E
STORE
Values: STORE
Pattern: \QSTORE\E
When a Jar is build it has default DEFLATE compression with the default level. Using this instruction you +can override the compression method.
+-compression: STORE
+Example: -conditionalpackage: com.*
Values: ${packages}
Pattern: .*
The -conditionalpackage instruction implements a feature that is either looked up in awe (when understood) or regarded with disgust. Though one has to be very careful with its use, it is a feature that can significantly reduce runtime dependencies. So what does it do? Well, it copies the content of packages from the current class path into your bundle that:
The purpose of this instruction is very much the same as static linking. Unix'es have a similar feature with static linking. It addresses the problem of util libraries. Utility libraries are extremely useful because they allow us to create primitives we can use in many places. However, in reality utility libraries are often quite, well let's say, all over the place. As a result utility libraries tend to drag in a lot of transient dependencies that are actually not all needed. As a common example, someone once used a single method from an Apache Commons library and dragged in 20 Mb of dependencies.
+The purpose therefore of the -conditionalpackage instruction is to pick cohesive packages from a utility library and copy them in the bundle. Any dependencies of those copied packages will also be copied if they match the selectors.
The packages that are copied cannot be exported, they must be private. This makes it possible to rely on the exact version that the bundle is compiled against. It also ensures that no information is leaked between bundles when statics are used.
+The given PACKAGE-SPEC follows the format outlined in Selector.
For example:
+-conditionalpackage: aQute.lib*
+will slurp any packages that have a name that matches aQute.lib* (e.g. both aQute.lib and aQute.libg) and are referred to by the current JAR's contents.
On the other hand the example:
+-conditionalpackage: mypackage.example.*
+will copy the package mypackage.example and all its sub-packages into the bundle in case they are referred to by the current JAR's contents.
A good example of a suitable library is the aQute.libg project. It is a collection of packages that each implement a single function. This ranges from a Strings class with simple String utilities, all the way to a Trajan graph analyzer and simple fork-join framework. Though some of the packages are dependent on each other, most packages have no dependencies whatsoever.
+When preparing a library for -conditionalpackage you should take the following into account:
One objection that is raised against this model is that you copy code. However, this model only copies the binaries, not source code, therefore, there is no real duplication. Yes, they then say, but when there is a bug I need to fix in many places? Well, the bundles will have to be rebuild but that is generally a good idea when a dependency changes. And think about it, that bundle was tested with the buggy code, it is highly unlikely that this bug seriously affects the bundle.
+Example: -conduit= jar/osgi.jar
Pattern: .*
The -conduit instruction allows a Project to act as a conduit to one or more actual JARs on the file system. That is, when the Project is build it will not build those JARs, it just returns them as the result of the project. This can be useful when a Project is moved elsewhere but must still be part of the build because, for example, it needs to be part of the release process.
-conduit: jar/foo.jar
+Notice that you can use the ${lsa} macro to get the contents of a directory:
-conduit: ${lsa;jar/*.jar}
+Example: -connection-settings= ~/.bnd/connection-settings.xml
Pattern: .*
This chapter discusses the connection settings of bnd that are used when bnd is asked to download or upload a file +from a remote server. The connection settings can provide a userid/password for basic authentication, proxies, and/or +the trust policy for verifying the host name of a TLS/SSL server. These settings can obviously not be part of the workspace +since they are unique to the actual user of the workspace. That is, they need to be stored outside the workspace +in a user accessible area.
+Since Maven is very popular we've followed the same syntax for settings. The supported elements in this file are
+<server> and <proxy>. Other elements are ignored.
The default order in which bnd looks for settings is:
+`~/.bnd/connection-settings.xml`
+`~/.m2/settings.xml`
+If you want to disable the use of default from the workspace then you can use the -connection-settings instruction
+in the workspace's cnf/build.bnd file. If you set the instruction to false then it will not look for the aforementioned files.
-connection-settings: false
+In this setting, you can also list additional files to parse that must be of the same syntax as the Maven settings file. The names
+maven and bnd are recognized as ~/.m2/settings.xml and ~/.bnd/connection-settings.xml respectively. For example, if
+we first want to look in the home directory in ~/foo/settings.xml and then in the bnd settings, we can set the -connection-settings to:
-connection-settings: ~/foo/settings.xml, bnd
+In addition, you could also specify the maven settings inline. For example:
+-connection-settings: \
+ server; \
+ id="http://server"; \
+ username="myUser"; \
+ password=${env;PASSWORD}
+You can create a log file specific for the connections by specifying:
+-connection-log: somefile.txt
+This file will contain the detailed trace output. The file given is relative to the working directory.
+The settings files have the following XML structure:
+<settings>
+ <proxies>
+ <proxy>
+ <id/>
+ <active> 'true' | 'false' </active>
+ <protocol> 'http'| 'direct' | 'socks' </protocol>
+ <host> domain name </host>
+ <port> port to use </port>
+ <username> user name to use for the proxy </username>
+ <password> password to use for the proxy </password>
+ <verify> true | false </verify>
+ <nonProxyHosts> glob ( '|' glob )* </nonProxyHosts>
+ </proxy>
+ </proxies>
+ <servers>
+ <server>
+ <id>default</id>
+ <username>username</username>
+ <password>password</password>
+ <verify> true | false </verify>
+ <trust> comma separated paths to X509 certificates </trust>
+ <maxConcurrentConnections>10</maxConcurrentConnections>
+ </server>
+ </servers>
+</settings>
+Any additional elements are ignored.
+The purpose of the proxy definitions is to define a communication proxy. We support HTTP and SOCKS proxies. An additional built-in proxy is the DIRECT 'proxy'. Proxies can be authenticated with a username and password.
| Tag | Default | Values | Description |
+|-----------------------------------------------------------------------------------------------|
+| id | default | NAME | Identifies the proxy, must be unique in |
+| | | | the list of proxies. |
+| active | true | true | false | If this proxy is active |
+| protocol | http | socks | http | direct | The proxy protocol to use. |
+| host | | domain-name | The domain name or IP address of the proxy host |
+| port | | INTEGER | Port number, maybe absent if default |
+| | | | port for protocol |
+| username | | | User name for authentication to the proxy |
+| password | | | Password for authentication to the proxy |
+| nonProxyHosts | none | GLOB ('|' GLOB)* | Filter on the destination hosts that should not be proxied |
In maven, the servers are identified by an id; when you define a repository you tell which id to use. In bnd this works
+slightly different. Instead of using the id we use the id in the settings as a glob expression on the protocol, host name,
+and port number. The glob expression must match the scheme, the host and the port if the port is not the default port.
+To match the url https://maven2-repository.dev.java.net/, the server component could look like (pay attention to the missing trailing slash):
<server>
+ <id>https://*java.net</id>
+ ...
+</server>
+The first server that matches the id will provide the parameters.
+| Tag | Default | Values | Description |
+|-----------------------------------------------------------------------------------------------|
+| id | | GLOB | A glob expression on the scheme + host + port |
+| username | | | User name for authentication to the server |
+| password | | | Password for authentication to the server |
+| verify | true | false | true | Enable/disable the verification of the host name against a certificate for HTTPS |
+| trust | | | Provide paths to certificate that provide trust to the host certificate. The format most of a X.509 certificate file. Normally the extension is .cer |
+| maxConcurrentConnections|0 (unlimited)|0.. | Limits the number of parallel connections to a host. Some hosts use the number of concurrent connections as a sign of a denial of service attack. |
It also supports Bearer (OAuth2) authentication. If the <server> configuration has only a password and no username, then Bearer authentication is in effect with the password used as the token.
-connection-settings: server;id="https://*.server.com";password="oauth2token"
+will cause
+Authorization: Bearer oauth2token
+request header to be sent to servers matching the glob https://*.server.com.
+See https://github.github.com/maven-plugins/site-plugin/authentication.html for an example of a <server> configuration for OAuth2.
The bnd command line provides a number of commands to display and verify the settings as well as getting the information from +getting a remote file.
+$ bnd com
+Available sub-commands:
+
+clear - Clear the cached file that is
+ associated with the givenURI
+info - Show the information used by the
+ Http Client to get aremote file
+settings - Show the bnd -connection-settings
+Certificate authentication is only suppprted using the Java settings through system properties. Either with -D on the command line
+or in the eclipse.ini file or through some other means. In that case, make sure not specify the <trust/> nor <verify/> element
+in a connection-settings.xml. The reason is that once you specify those element, the built-in Java mechanism is replaced with bnd code.
Example: -consumer-policy: ${range;[==,+)}
Pattern: .*
The -consumer-policy instruction defines the semantic versioning policy to be used when a type is a consumer. A consumer is in general a type that is implemented by classes that are just users of the package. In contrast, a provider is the party that is basically responsible for the contract defined in the package. For example, when you implement Event Admin, the org.osgi.service.event package is your responsibility so the types you need to implement like EventAdmin are provider types. (These types are annotated with a @ProviderType annotation.) A casual user of the Event Admin service will be a consumer, the EventHandler type is therefore annotated with a@ConsumerType.
The purpose of this distinction is semantic versioning. It turns out that the relation between a consumer and a provider is not symmetric. A provider is tightly bound to a contract while a consumer is expected to have backward compatibility. Virtually any change to the contract requires the provider to adapt while a consumer is in almost all cases protect against changes.
+This asymmetry has a consequence for the semantic versioning. In the OSGi, the semantics are defined that a micro change does not affect the provider nor the consumer. A minor change affects the provider, and a major change affects both. Therefore, a bundle that implements a provider type must import a range from major.minor.micro ... major.minor+1.0. A bundle that implements a consumer type must import major.minor.micro ... major+1.0.0.
In theory, bnd could have hard coded these policies but there are always cases where the policy is just not right. The -consumer-policy specifies the macro to use for calculating the version range. The default definition is:
-consumer-policy ${range;[==,+)}
+-provider-policy ${range;[==,=+)}
+The range macro works very much like the version macro. It uses a template to define a change the range/version.
+The provider and consumer policy are global and this is not very convenient if you want to make an exception just for a specific bundle. For example, a bundle coming from Gavin King's Ceylon. For this reason, you can also specify a policy on an import:
+Import-Package com.gavinking.*;version="${range;[--,++)}", *
+The counterpart of the -consumer-policy is of course the -provider-policy.
Example: -contract!Servlet,*
Pattern: .*
Though the OSGi has a very elegant package version model there are still many that think this is too much work. They do not want to be bothered by the niceties of semantic versions and just want to use, let's say, Servlet 3.0. For those people (seemingly not interested in minimizing dependencies) the OSGi Alliance came up with contracts in OSGi Core, Release 5.0.0. A contract allows you to:
+This very common pattern is called the Capability/Requirement (C/R) model in OSGi, it underlies all of its dependency concepts like Import/Export package and others; it forms the foundation of the OSGi Bundle Repository. If you ever want to know what is happening deep down inside a framework than look at the Wiring API and you see the requirements and capabilities in their most fundamental form.
+Capabilities declare a set of properties that describe something that a bundle can provide. A Requirement in a bundle has a filter that must match a capability before this bundle can be resolved. To prevent requirements matching completely unrelated capabilities they must both be defined in the same namespace, where the namespace then defines the semantics of the properties. Using the C/R model we were able to describe most of the OSGi dependencies with surprisingly few additional concepts. For a modern OSGi resolver there is very little difference between the Import-Package and Require-Bundle headers.
So how do those contracts work? Well, the bundle that provides the API for the contract has a contract capability. What this means is that it provides a Provide-Capability clause in the osgi.contract namespace, for example:
# Bundle P:
+Provide-Capability:\
+ osgi.contract;\
+ osgi.contract=Servlet;\
+ uses:="javax.servlet,javax.servlet.http";\
+ version:Version="3.0"
+Export-Package: javax.servlet, javax.servlet.http
+This contract defines two properties, the contract name (by convention this is the namespace name as property key) and the version. A bundle that wants to rely on this API can add the following requirement to its manifest:
+# Bundle R:
+Require-Capability:\
+ osgi.contract;\
+ filter:="(&(osgi.contract=Servlet)(version=3.0))"
+Import-Package: javax.servlet, javax.servlet.http
+Experienced OSGi users should have cringed at these versionless packages, cringing becomes a gut-reaction at the sight of versionless packages. However, in this case it actually cannot harm. The previous example will ensure that Bundle P will be the class loader for the Bundle R for packages javax.servlet, javax.servlet.http. The magic is in the uses: directive, if the Require-Capability in bundle R is resolved to the Provide-Capability in bundle P then bundle R must import these packages from bundle P.
bnd has support for this. First bnd can make it easier to create the Provide-Capability header since the involved packages are in the Export-Package as well as in the Provide-Capability headers. The do-no-repeat-yourself mantra dictated am ${exports} macro. The ${exports} macro is replaced by the exported packages of the bundle, for example:
# Bundle P:
+Provide-Capability:\
+ osgi.contract;\
+ osgi.contract=Servlet;\
+ uses:="${exports}";\
+ version:Version="3.0"
+Export-Package: javax.servlet, javax.servlet.http
+Furthermore there is the -define-contract instruction which can be applied in order to define a contract which is not available on the build path.
+That said, the most extensive help you get from bnd is for requiring contracts. Providing a contract is not so cumbersome, after all you're the provider so you have all the knowledge and the interest in providing metadata. Consuming a contract is less interesting and it is much harder to get the metadata right. In a similar vein, bnd analyzes your classes to find out the dependencies to create the Import-Package statement, doing this by hand is really hard (as other OSGi developing environments can testify!)
So to activate the use of contracts, add the -contract instruction:
# bnd.bnd:
+-contract: *
+This instruction will give bnd permission to scan the build path for contracts, i.e. Provide-Capability clauses in the osgi.contract namespace. These declared contracts cause a corresponding requirement in the bundle when the bundle imports packages listed in the uses clause. In the example with Bundle R, bnd will automatically insert the Require-Capability header and remove any versions on the imported packages.
Sometimes the wildcard for the -contract instruction can be used to limit the contracts that are considered. Sometimes you want a specific contract but not others. Other times you want to skip a specific contract. The following example skips the 'Servlet' contract:
bnd.bnd:\
+ -contract: !Servlet,*
+Note: As of bnd 4.1.0 the default value for the -contract instruction will be * which result in the automatic application of any contracts found at build time.
The ContractTest.java provides some examples for people that want to have a deeper understanding.
+Example: -define-contract:\
+ osgi.contract;\
+ osgi.contract=JavaServlet;\
+ uses:="javax.servlet,javax.servlet.annotation,javax.servlet.descriptor,javax.servlet.http";\
+ version:Version="3.0"
Pattern: .*
Used in conjunction with the -contract instruction,-define-contract can be applied in order to define a contract which is not available on the build path (i.e. compile class path).
The contract specification is exactly the same syntax used in the Provide-Capability header.
-define-contract:\
+ osgi.contract;\
+ osgi.contract=JavaServlet;\
+ uses:="javax.servlet,javax.servlet.annotation,javax.servlet.descriptor,javax.servlet.http";\
+ version:Version="3.0"
+Now if the current bundle imports packages declared in the uses directive of the defined contract above, they will be imported without a package version, and a contract requirement will be added exactly as if there had been a contract on the build path.
Note the -contract instruction defaults to * (all contracts) and so it can be omitted when using the -define-contract instruction.
Example: -dependson=org.acme.cm
Values: ${projects}
Pattern: .*
Projects referenced by -buildpath are always built first but sometimes
+you need to specify projects to be build first which are not referenced by -buildpath.
+You can specify those additional projects using -dependson.
-dependson: projA, projB
+Example: -deploy=mavenrepo
Values: ${repos}
Pattern: .*
The -deploy instruction is used to deploy the current project to a repository using deploy plugins, such as the MavenDeploy plugin. When this instruction is set, bnd will attempt to deploy the project's build outputs to the specified repositories.
You must specify which repositories to deploy to using the appropriate configuration. If no repositories are set, deployment will not occur and a warning will be issued. This instruction is typically used in conjunction with plugins that handle the actual deployment process.
+TODO Needs review - AI Generated content
+Example: -deployrepo=cnf
Values: ${repos}
Pattern: .*
The -deployrepo instruction is used to deploy a bundle (JAR file) to a specific repository using deploy plugins, such as the MavenDeploy plugin. You can specify the repository by name, and the bundle will be uploaded to that repository if it supports write operations.
If no repository name is provided, the first writable repository found will be used. If no suitable repository is found, deployment will fail with an error. This instruction is typically used in conjunction with plugins that handle the actual deployment process.
+Note: This feature may require additional configuration and plugin support to work as intended.
+TODO Needs review - AI Generated content
+Example: -diffignore=Bundle-Version
Pattern: .*
You can use the -diffignore instruction to specify manifest header names
+and resource paths to ignore during baseline comparison.
-diffignore: com/foo/xyz.properties, Some-Manifest-Header
+Example: -diffpackages=!*.internal.*, *
Pattern: .*
You can use the -diffpackages instruction to specify the names of exported packages
+to be baseline compared. The default is all exported packages.
The following attributes can be specified on each package selector:
+Specifies the minimum change level that should be reported. Valid values are MICRO, MINOR, or MAJOR. Changes below this threshold will be ignored during baselining.
Example:
+-diffpackages: *;threshold=MAJOR
+This will only report MAJOR changes and ignore MINOR and MICRO changes.
+Exclude internal packages from baselining:
+-diffpackages: !*.internal.*, *
+Set threshold for changes:
+-diffpackages: *;threshold=MINOR
+Example: -digests: SHA-1
Pattern: .*
The -digests instruction allows you to specify which digest (checksum) algorithms should be used when generating JAR files. You can provide a comma-separated list of algorithms, such as SHA-1, MD-5, or others supported by your environment.
By default, if no value is specified, both SHA-1 and MD-5 are used. Setting this instruction ensures that the generated JAR includes the specified digests, which can be useful for verifying file integrity or meeting repository requirements.
TODO Needs review - AI Generated content
+Example: -distro: karaf-4.1.jar;version=file
Pattern: .*
The -distro instruction is used in the case that your application must run in a host environment, for example Karaf. In such cases it is not possible to calculate the system capabilities from the framework and the run path. Each of these host environments has a specific set of capabilities that should be used as input to the resolver.
The -distro capability has the same syntax as the -runpath, a list of bundle specifications. The resolver will parse these bundles and treat their capabilities specified with the Provide-Capability header in their Manifest as the system capabilities. These files can be generated manually. However, the bnd command line tool can create a distro bundle using the remote agent.
When the -distro is present in the bndrun file it overrides any other definition that are used to derive capabilities. If additional capabilities are needed the -runprovidedcapabilities should be used.
For example:
+-distro: karaf-4.1.jar;version=file
+Example: -donotcopy=(CVS|\.svn)
Pattern: .*
When -includeresource copies files from another JAR or the file system it will look at the -donotcopy
+instruction. This instruction is a single regular expression. Any resource that is copied is matched
+against this list. If there is a match, then the file is ignored.
For example (and also the defaults)
+-donotcopy: CVS|\\.svn|\\.git|\\.DS_Store|\\.gitignore
+Example: -dsannotations: *
Pattern: .*
The -dsannotations instruction tells bnd which classes in your bundle should be scanned for Declarative Services (DS) annotations. bnd will process these classes and generate the necessary DS XML descriptors automatically.
You provide a comma-separated list of fully qualified class names or use * to process all classes. This makes it easy to enable DS annotation processing for your entire bundle or for specific classes only.
You can further configure DS annotation processing using the -dsannotations-options instruction.
When bnd processes DS annotations, it validates that component classes meet the DS specification requirements. Starting with bnd 7.3.0, component classes must have either a public no-argument constructor or a public @Activate-annotated constructor. Inner classes must be declared as static.
If validation fails, bnd will generate an error and stop the build. See Component Class Requirements for detailed requirements and examples.
+ +TODO Needs review - AI Generated content
+Example: -dsannotations-options: version;minimum=1.2.0
Values: (inherit|felixExtensions|extender|nocapabilities|norequirements|version)
Pattern: .*
The -dsannotations-options instruction configures how DS component annotations are processed and what metadata is generated.
-dsannotations-options: version;minimum=1.2.0;maximum=1.3.0
+The example above, will restrict the use of OSGi DS annotations to minimum 1.2.0 and maximum 1.3.0 version. The version number denotes that the users are free to use any version equal to or higher than 1.2.0 but less than or equal to 1.3.0, provided that the users have the DS annotations included on the build path.
+The following options are supported:
+| option | ++ |
|---|---|
| inherit | +use DS annotations found in the class hierarchy of the component class. WARNING: Annotations are not inherited from the component's super classes by default. The problem is that super classes from imported packages may be different at runtime than they were at build time. So it is always best to declare your annotations on the actual component class. | +
| felixExtensions | +enable features proprietary to Apache Felix SCR | +
| extender | +add the osgi.extender=osgi.component requirement to the manifest |
+
| nocapabilities | +do not add osgi.service capabilities to the manifest |
+
| norequirements | +do not add osgi.service requirements to the manifest |
+
| version | +set the minimum and/or maximum version of the osgi.extender=osgi.component requirement added to the manifest |
+
Example: -eeprofile: name="a,b,c"
Values: name="a,b,c", auto
Pattern: .*
The -eeprofile instruction allows you to control which Java 8 (or later) profile is used for your project. A profile is a set of Java packages that enables the Java Virtual Machine to be delivered in smaller, more targeted versions.
You can set -eeprofile to auto to let bnd automatically determine the appropriate profile based on the packages your project uses. Alternatively, you can specify one or more profiles explicitly by name. If your project references packages outside the selected profiles, no profile will be set.
This instruction is useful for optimizing your bundle for specific Java environments and ensuring compatibility with the intended runtime profile.
+TODO Needs review - AI Generated content
+Example: -executable: rejar=STORE,strip=*:OSGI-OPT/*
Pattern: .*
strip Strips files from embedded JARs. The syntax JARPATHMATCH ':' RESOURCEPATHMATCH, both globs.Example: strip=*:OSGI-OPT/*
Pattern: .*
location A pattern to form the location for the bundle. This pattern is processed by the macro processor. @bsn is the bsn, and @version is the version, and @name is the file name. If no pattern is given, the file name is used to make a unique name in the /jar directory. If multiple bundles end up with the same name then the last one wins.The expansion may not contain file separators like /.If the storage area is not cleaned, use the example pattern
Example: location=location='${@bsn}-${version;=;${@version}}.jar'
Pattern: .*
rejar Re-jar the -runpath and -runbundles to the given compression. If not set, bundles are not touched. This should not change the signatures
Example: rejar=rejar=STORE
Values: DEFLATE,STORE
Pattern: .*
Options:
+DEFLATEValues: DEFLATE
Pattern: \QDEFLATE\E
STORE
Values: STORE
Pattern: \QSTORE\E
Bundles are stored in a jar directory in the executable JAR. The name in this directory is by default the file name in the repository. This filename is, however, also used by the launcher as the location. This implies that if the file name contains the version, a new location is created when the version changes. This ends up with too many duplicates quickly. This is only a problem when you keep the storage area.
The location option in the -executable instructions can provide a printf format to calculate a location based on the Bundle-SymbolicName and Bundle-Version.
For example:
+-executable: location='${@bsn}-${version;=;${@version}}'
+This format will create locations where a bundle will overwrite when the major part of the version is the same. I.e. com.example.foo-1. I.e., it will allow multiple bundles that are semantically incompatible but compatible bundles overwrite each other.
When an executable JAR is created by the Project Launcher the compression is controlled with the -compression +instruction. However, this über JAR contains bundles and JARs for the run path. Since executable JARs are sometimes used in +embedded environments it is important that they are small and the code can loaded quickly. It is difficult to +have defaults for this since there are many aspects:
+In such a complex trade off there are no singular answers. This -executable instruction allows therefore to
+set the different options and then benchmark the result.
Experience shows that the size can be minimized by rejarring the inner bundles and JARs with the STORE compression +but using DEFLATE for the outer/über JAR. This decreases the size because the compression algorithm then works on +all class files of all bundles. This is much more efficient for the DEFLATE algorithm then compressing each class file +on its own. Gains seen are 20%-30%.
+The rejar option in the -executable instruction can take the values STORE or DEFLATE. This will open
+the embedded JARs and write them in the given compression in the über JAR.
For example:
+-executable: rejar=STORE
+The default is to not touch the bundle.
+The strip option can be used to strip resources from JARs embedded in the execetable. Its parameter can define
+a list of two GLOBs. The first GLOB must match the file name of the resource and the second GLOB must match the
+resource name in the bundle. GLOBs are separated with a colon (':'). If no colon is present then the first GLOB
+is assumed to be the wildcard GLOB that matches anything.
Syntax:
+strip ::= matcher ( ',' matcher )*
+matcher ::= ( GLOB ':' ) GLOB
+Some examples:
+*.map – Match all resources ending in .mapOSGI-OPT/* – Remove the optional resources in bundlecom.example.some.bundle.*:readme.md – Remove the readme's from bundles withe file name matching com.example.some.bundle.*.Note hat if you combine multiple GLOBs then you must separate them with a comma (',') and that implies the total must be +quoted with single or double quotes. If it is a single GLOB (pair) then quoting is optional. For example:
+-executable: rejar=STORE, strip='OSGI-OPT,*.map'
+The default is to not strip anything.
+Rejarring and stripping should work for unsigned bundles since the signatures should not be affected by the +compression algorithms used.
+Example: -export: launcher.jar
Values: FILE ( ';' PARAMETER )* ( ',' FILE ( ';' PARAMETER )* )*
Pattern: .*
The -export instruction can be used to export a bndrun or bnd file to an executable JAR during the build process. In contrast to the export functions, this -export instruction is supported in all drivers, including Eclipse. It can be used to continously build executable JARs or other supported exporters. It can only be used in the top level bnd.bnd file.
The format of the -export instruction is:
-export ::= filespec ( ',' filespec )*
+filespec ::= path (';' PARAMATER )*
+The path is either a relative path in the project directory or it is a wildcard specification using globs. All files in the project directory are selected. In general, these should be bnd or bndrun files.
The following attributes are architected:
+type – The type specifies the exporter type. The default type is bnd.executablejar.pack.name – Overrides the default name of the output file. It should contain the extension. The file should not have path segments, it will be placed in the target directory. Without a name, the exporter defines the file name since the extensions can differ depending on the exporter.bsn – Set the bundle symbolic name of the resulting JAR file if possibleversion – Set the version of the resulting JAR file if possible-profile can be used to select a specific property profile.The -exporttype can be used to set some default attributes for a specific type.
The Exporters use a plugin mechanism and therefore the list is not closed. The following exporters are supported by bnd out of the box:
+bnd.executablejar.pack – Exports a JAR file using the launcher from the -runpath or the default if no launcher is specified.bnd.executablejar – Similar to the previous but does not support profiles, has no automatic bsn assigned, and entries are not signed. The reason there are two types for the more or less the same format with subtle differences is for backward compatibility.bnd.runbundles – A JAR with the runbundles. As optional Attributes targetDir will output all runbundles in the set directory in the resulting jar and template will use a given jar/zip or directory as template to which the bundles will be added.osgi.subsystem.application – Export into an application subsystemosgi.subsystem.feature – Export into a feature subsystem osgi.subsystem.composite – Export into a composite subsystemp2 – Export a P2 repositoryFor example:
+-export: \
+ foo.bndrun; \
+ -profile=debug; \
+ -runkeep=true; \
+ name = "foo.jar, \
+ bar.bndrun
+If the filespec clause does not set the type nor the name then it is assumed the backward compatible mode is required. This will set the output name to the file name with a .jar extension and it will set the bundle symbolic name.
Example: -export-apiguardian=!*impl*,*;version=3.0
Pattern: .*
API Guardian is a
+++Library that provides the
+@APIannotation that is used to annotate public types, methods, constructors, and fields within a framework or application in order to publish their status and level of stability and to indicate how they are intended to be used by consumers of the API.
The heuristic used by bnd to determine which packages to export is as follows:
+-export-apiguardian is required (i.e. opt-in is required). The value of the instruction is a package specification like most other bnd instructions. The minimal configuration is -export-apiguardian: * which instructs bnd to scan all classes in all packages with the plugin@API annotationstatus using the highest value of org.apiguardian.api.API.Status (in ordinal order) found on @API annotations in the packagemandatory:=status for any package whose highest value of org.apiguardian.api.API.Status found was INTERNAL . This implies that the package can only be imported by marking the import with the attribute status=INTERNAL Export all packages annotated with @API using version 1.2.3:
-export-apiguardian: *;version="1.2.3"
+Export only packages annotated with @API named com.acme.foo.* but not com.acme.foo.bar.*
-export-apiguardian:\
+ !com.acme.foo.bar.*,\
+ com.acme.foo.*
+Example: -exportcontents=!*impl*,*;version=3.0
Pattern: .*
Exports the given packages but does not try to include them from the class path. The packages should be loaded with alternative means. The syntax is similar to the Export-Package header.
Export-Package = -includepackage + -exportcontents (source).
That is, Export-Package will add packages to the bundle, perhaps from (other) .jar files on the classpath, and also add those packages to the Export-Package manifest header.
-exportcontents will only add packages which are already part of the bundle to the Export-Package manifest header.
-exportcontents: com.example.api;
+See the packages macro, which is useful in combination with -exportcontents.
Note: By default bnd automatically calculates Import-Package references for exported packages. This is called package substitution
+You can use the -noimport:=true directive which instructs bnd to not calculate Import-Package references for exported packages.
Example:
+`
+-exportcontents: com.*;-noimport:=true
With the -nosubstitution: true instruction, this substitution behavior can be disabled globally.
+So -exportcontent is appropriate for Maven and Gradle (non-Bnd workspace) builds where the content of the bundle is being managed by normal Maven or Gradle means.
Example: -exportreport: metadata.json
Pattern: .*
The purpose of the -exportreport instruction is to configure a list of reports of the current state of the workspace and/or its projects, which can then be exported by the build tool. The primary usage is to automate the documentation of projects. An introduction to this feature can be found here.
See -exportreport command documentation to export the reports.
+-exportreport ::= report-def ( ',' report-def ) *
+report-def ::= path ( ';' option ) *
+option ::= scope | template | templateType | parameters | locale | configName
+scope ::= 'scope' '=' ( 'workspace' | 'project' | EXTENSION )
+template ::= 'template' '=' ( path | url )
+templateType ::= 'templateType' '=' ( 'xslt' | 'twig' | EXTENSION )
+parameters ::= 'parameters' '=' '"' parameter ( ',' parameter )* '"'
+locale ::= 'locale' '=' <language> ( '-' <country> ( '-' <variant> )? )?
+configName ::= 'configName' '=' extended
+This is a merged instruction.
+The most simple configuration is to generate a file which will contain all the OSGI headers, metatypes declarations, declarative services, ... of a bundle built by a project:
+bnb.bnd
+-exportreport: metadata.json
+an XML file can also be generated
-exportreport: metadata.xml
+The scope attribute allows to define all the reports in a common place and to target the source of the extracted data. In Bnd, the two possible values are workspace and project but other tools (such as Maven) could define their own scopes.
For example, the following configuration will generate a report at the workspace root, which will aggregate data of all the projects, and one report per project:
+build.bnd
+-exportreport: \
+ ${workspace}/jekyll_metadata.json;scope=workspace, \
+ metadata.json;scope=project
+The template attribute allows to specify a path or an URL to a template file. In Bnd, two template types are accepted: XSLT and TWIG.
For example, a template can be used to generate a markdown file:
+bnd.bnd
+-exportreport: readme.md;template=/home/me/templates/readme.twig
+If the extension file is missing, the templateType attribute can be used to indicate the template type:
bnd.bnd
+-exportreport: webpage.html;template=http:<...>/f57ge56a;templateType=xslt
+If the template file needs to be parametrized, the parameters attribute can be used to provide a list of parameters and their values:
bnd.bnd
+-exportreport: \
+ webpage.html; \
+ template=<...>/template.xslt; \
+ parameters="oneKey=<path to other template>,otherKey=api-bundle"
+++If a file with the same name as the exported report but with a template file extension is found in the same folder as the exported report, this file will be used to transform the report instead of the optionally defined
+templateattribute. This allows to quickly customize a report without redefining an inherited instruction.
The locale attribute can be used to extract the data for a specific locale. For example, if a bundle defines some metatype description in French:
bnd.bnd
+-exportreport: report.json;locale=fr-FR
+In some case, it may be necessary to control what data should be present in the report, for example if you use external plugins to contribute to the extraction and aggregation phase. For this you can use the -reportconfig instruction to create named configuration of reports.
With the configName attribute you can reference the configuration name that will be used to extract and aggregate the data of the report.
See the -reportconfig instruction documentation for more information.
+Reports are generated from workspaces and projects. However, in case of sub-projects, it may be necessary to generate a file for each built bundle and to know what bundle must be process by the template engine. For this, you can define a paramater in your template file and provide a different value for each bundle, for example:
+build.bnd
+-exportreport: \
+ readme.md;template=<...>/readme.twig;scope=project
+bnd.bnd
+-exportreport.sub: \
+ mybundle.api.md; \
+ template=<...>/readme.twig; \
+ parameters="currentBundle=mybundle.api", \
+ mybundle.provider.md; \
+ template=<...>/readme.twig; \
+ parameters="currentBundle=mybundle.provider"
+For this specific multi-project, this will generate three files:
+Example: -exporttype=bnd.executablejar;foo=bnd, bnd.runbundles;bar=bnd
Pattern: .*
.*The -extension instruction allows you to specify a plugin that is loaded from a URL. The plugin provides a header used to instantiate the extension. This is useful for extending bnd's functionality with custom plugins or features that are not included by default.
Example:
+-extension: com.example.MyExtension;version=1.0.0
+The extension will be downloaded and loaded as part of the build process.
+TODO Needs review - AI Generated content
+Example: -failok=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -failok instruction allows the build to continue even if errors occur. When set to true, bnd will ignore any errors during the build process and assume everything went fine. This can be useful for experimental or non-critical builds, but should be used with caution as it may hide real problems.
Example:
+-failok: true
+By default, this option is not set and build errors will cause the process to fail.
+TODO Needs review - AI Generated content
+Example: -fixupmessages: 'Version mismatch';replace:='************* ${@}';restrict:=error
Pattern: .*
The -fixupmessages instruction is intended to fixup the errors and warnings. It allows you to remove errors and/or warnings, turn errors into warnings, and turn warnings into errors. With this instruction you can fail a build based on a warning or succeed a build that runs into errors.
The default of this instruction is to list a number of patterns. Any error or warning that matches this pattern is then removed. The following example will remove any error/warning that matches 'some error', 'another error', or 'and yet another error'.
-fixupmessages: \
+ some error,
+ another error,
+ and yet another error
+The pattern is a SELECTOR, which makes it possible to do case insensitive matches, wildcards, literals, etc.
+The basic format of -fixupmessages is:
-fixupmessages ::= fixup ( ',' fixup ) *
+fixup ::= SELECTOR directive *
+directive ::= ';' ( restrict | is | replace )
+restrict ::= 'restrict:=' ( 'error' | 'warning' )
+is ::= 'is:=' ( 'ignore' | 'error' | 'warning' )
+replace ::= 'replace:=' <<text>>
+The value of a fixup clause is a globbing expression.
+The following directives are supported:
+restrict: – By default, the fixup clause is applied to all errors and warnings. You can restrict its application to either errors or warnings specifying either restrict:=error or restrict:=warning.is: – By default an error remains an error and a warning remains a warning. However, if you specify the is: directive you can force an error to become a warning or vice versa. This can be very useful if you build fails with an error that you do not consider a failure.replace: – Replace the message with a new message. The replacement will be processed by the macro processor, the ${@} macro will contain the current message.The -fixupmessages instruction is a merged property. This means that you can define it in many different places like for example in a file in cnf/ext. Just put an extension on the instruction. For example:
-fixupmessages.one: 'Some error'
+-fixupmessages.two: 'Another error'
+# Turn an error into a warning
+-fixupmessages:
+ 'Invalid character'; \
+ restrict:=error;
+ is:=warning
+
+# Replace a message
+-fixupmessages: \
+ "split";replace:=broken
+
+# Ignore case by appending :i
+-fixupmessages: \
+ "case insensitive:i"
+
+# Wildcards
+-fixupmessages: \
+ "prefix*suffix"
+
+# Turn properties parser messages into warnings
+-fixupmessages.parser: \
+ "Invalid character in properties"; \
+ "No value specified for key"; \
+ "Invalid property key"; \
+ "Invalid unicode string"; \
+ "Found \\<whitespace>";
+ is:=warning
+Example: -generate: \
+ gen/**.java; \
+ output='src-gen/' ; \
+ generate='javagen -o src-gen gen/'
Pattern: .*
Virtually all the work bnd is concerned about happens in generating the JAR file. The key idea is to pull resources in the JAR, instead of the more traditional push model of other builders. This works well, except for generating source code. This generating step must happen before the compiler is called, and the compiler is generally called before bnd becomes active.
+This -generate instruction specifies the code generating steps that must be executed. Source code can be generated by system commands or the bnd external plugins.
-generate ::= clause ( ',' clause )*
+clause ::= FILESET ';' 'output=' DIR (';' option )*
+src ::= FILESET
+option ::= 'system=' STRING
+ | 'generate=' STRING
+ | 'classpath=' PATH
+ | 'workingdir=' FILE
+ | 'clear=' BOOLEAN
+ | 'version=' RANGE
+For each clause, the key of the clause is used to establish an Ant File Set, e.g. foo/**.in. This a glob expression with the exception that the double wildcard ('**') indicates to any depth of directories. The output attribute must specify a directory. If the output must be compiled this directory must be on the bnd source path.
The output directory will created if it does not exist. It will be cleared of any previous generate results before a run, except if the option clear is set to false. In this case the used Generator needs to deal with the remnants in the directory itself.
If any file in the source is older than any file in the target (to any depth), or the target is empty, the clause is considered stale. If the clause is not stale, it is further ignored. If no further options are set on the clause, a warning is generated that some files are out of date.
+If either a warning or error option is given, these will be executed on the project.
+If a command STRING is given it is executed as in the ${system} macro. If the command STRING starts with a
+minus sign (-) then a failure is not an error, it is reported as warning.
The generate option will execute an external plugin or plain JAR with a Main-Class manifest header. The choice is made by looking at the first word in the generate attribute.
If this name has a dot in it, like in a fully qualified class name, it is assume that species a class name. (If the name starts with a dot, it will be assume to be a name in the default package.) In this case, the classpath attribute of the instruction can be used to provide additional JARs on the command's classpath. The format of PATH is the standard format for instructions like -buildpath. In this case, you can also set the workingdir to a directory. This directory is specified relative to the project.
Without a dot in the name, the name is assumes to be an external plugin name, with the objectClass (service type) of Generator<? extends Options>. External , or Main-Class jars, can come from an external repository or a local workspace project.
The generate value is a command line. It can use the standard unix like way of specifying a command. It supports flags (boolean parameters) and parameter that take a value. When this external plugin is executed, it is expected to create files fall within the target, if not, an error is reported. These changed or created files are refreshed in Eclipse.
The command line can be broken in different commands with the semicolon (';'), like a unix shell. Redirection of stdin ('<'), stdout ('>', or '1>'), and stderr ('2>') are supported. The path for redirection is relative to the project directory, even if workingdir has been specified.
With the version version range attribute it is possible to restrict the candidates if there are multiple versions available. The code will select the highest version if only one is used.
Include in the bnd build is a javagen external plugin that is useful to generate Java code based on build information. It uses a template directory with Java files. When the external plugin runs, it will use all these files to write matching Java files in the output directory, in a matching package directory. The input Java files can be prefixed with a properties header:
+---
+foo: 1
+---
+package foo.bar;
+class Foo {
+ int foo = ${foo};
+}
+The optional header is removed and then the remainder of the file is ran through the bnd macro processor.
+If this example is used, it is necessary to add a new source folder. In Eclipse, this requires adding an entry in the .classpath file, in bnd it requires the modification of the src property.
src=${^src},src-gen
+Assuming that the input files are in the gen directory, the following can be used to automatically generate the output files based on the input files.
-generate: \
+ gen/**.java; \
+ output='src-gen/' ; \
+ generate='javagen -o src-gen gen/'
+JFlex and CUP are popular tools to create lexers and parsers. The JARs are on Maven Central with Main-Class manifest attribute. The JFlex JAR, however, requires the cup_runtime JAR on the classpath.
We can directly use these executable JARs with the -generate instruction.
-generate: \
+ lex/Foo.lex; \
+ output = gen-src/ \
+ generate = `jflex.Main -d lex-gen/ lex/Foo.lex'; \
+ classpath = 'de.jflex:cup_runtime;version=11b-20160615'
+Notice that we use the maven GAV format here to find the cup_runtime because these JARs are unfortunately not bundles.
It is possible to create an External Plugin or a Main Class command your in the same workspace as where you apply the command. This makes it easy to develop commands interactively. The easiest way is to make an External Plugin. The support for Main-Class is mostly to support existing JAR.
+The following is an Generate external plugin that outputs "hello":
+@ExternalPlugin(name = "hello", objectClass = Generator.class)
+public class Hello implements Generator<HelloOptions> {
+
+ public interface HelloOptions extends Options {
+ boolean upper();
+ String name(String defaultName)
+ }
+
+ public Optional<String> generate(
+ BuildContext context,
+ HelloOptions options) throws Exception {
+ String hello = "Hello " + options.name("World");
+ if ( options.upper() )
+ hello = hello.toUpperCase();
+
+ System.out.println( hello );
+ return Optional.empty();
+ }
+}
+The @ExternalPlugin annotation creates an external plugin capability in the bundle's manifest. The name is the name we can use as the command name. Do not use a name that has dots in it, this will then be confused with a main class command.
The Hello class implements Generator<HelloOptions>. This is the type the generate code expects.
The type parameter specifies a specification interface. This interface is used to specify the command line. A boolean method is a flag, and any other type is an option. The first character of the method is the name of the flag or option. For example, boolean upper() is a flag and has the -u and --upper. Flags can be combined. For example, if you have a flag -a and -b then you can also use -ab.
Options take parameters. For example, a String name() option would be set as -n World. The method on the spec interface can optionally take an argument, this argument is used as default if the option is not used in the command line. The argument type and the return type can be different. For example, if the return type is File, then the parameter type can be String so that the returned File is resolved against the base directory of the build.
Options that return File will resolve the input against the project directory. A Setgen/**.java.
The Options interface specifies a number of additional keys:
+The actual code is in the generate(BuildContext,HelloOptions) method. the Build Context contains useful context information.
It is ok to write to the System.out and System.err. The output is captured and can be redirected in the command line in the standard Unix way.
+The code is expected to return an Optional.empty() when everything is ok. If something is wrong, an actualString can be used to explain the error. Return a non-empty fails the call and the error will be reported.
If you the plugin source code is in the same workspace as the project using this plugin, you must make sure that the external plugin project is build before the project that uses it. You can achieve this with -dependson.
+You can take a look at the JavaGen project in the bnd build to see how an actual external plugin is made.
+Example: -gestalt=interactive
Values: (interactive|batch|ci|offline|shell)
Pattern: .*
Example: -groupid=com.foo.bar
Pattern: .*
The -groupid instruction defines the default Maven groupId to be used when generating POM resources and releasing to the Maven Bnd Repository Plugin.
If not specified, the value of -groupid defaults to the Bnd workspace folder name.
Also see the -pom and -maven-release instructions.
+Example: -include: -${java.user}/.bnd
Pattern: .*
-include InstructionThe -include instruction allows you to include the contents of other files (such as properties or manifest files) into your current configuration. You specify the path or URL of the file to include. The included file's properties will overwrite existing properties by default, unless you use the ~ prefix, which prevents overwriting. If the file or URL does not exist and you use the - prefix, no error will be reported.
The -include instruction is processed before any other properties, so it cannot use properties defined later in the same file. However, it can use properties defined by a parent configuration. This makes it possible to share common settings across multiple projects or workspaces.
You can use -include as follows:
-include: <path or url>
+This will read the path or url as a properties or manifest file (if it ends in .MF).
It is important to realize that the include is not handled by the parser.
+That is, it is not a normal text include.
+The properties parser will read all properties in one go and then the Properties object is inspected for the -include instruction.
+The paths or URLs in the -include instruction are processed one by one in order.
+By default, the properties in the included file overwrite the properties that were set in the same file as the -include instruction.
+If a property is already defined and not set to be overwritten (see below), the property will get a namespace assigned.
+The namespace will be derived from the filename or the last segment of the URL.
The -include instruction is processed before anything else in the properties.
+This means the -include instruction cannot use any properties defined in the same properties file to define the include paths.
+It can use properties already defined by a parent.
+So a Project's bnd.bnd file can have an -include instruction use properties defined in the Workspace (cnf/build.bnd and cnf/ext/*.bnd).
+For the Workspace, cnf/build.bnd, -include instruction can only use the default properties of Bnd.
+The Workspace cnf/ext/*.bnd files are processed after cnf/build.bnd.
+So cnf/ext/*.bnd files can have -include instructions which use properties set in cnf/build.bnd.
There are two possible options. The path/URL starts with a:
+~ – Included properties do not overwrite any existing properties having the same property names.- – If file or URL or path does not exist then do not report an error.# Read an optional file in the user's home directory
+-include -${user.home}/.xyz/base.bnd
+
+# Read a manifest
+-include META-INF/MANIFEST.MF
+
+# Use a URL
+-include https://example.com/foo/bar/setup.bnd
+
+# Read several
+-include first.bnd, second.properties
+
+# Don't overwrite any existing properties (my.prop, will not be overwritten by my.prop in no.overwrite)
+my.prop = don't overwrite
+-include ~no.overwrite
+TODO Needs review - AI Generated content
+Example: -includepackage: -includepackage com.example.foo.*
Pattern: .*
The -includepackage instruction falls in the family of instructions to pull in packages from the current class path. The
+instructions lists one of more package specifications that can contain wildcards. Any attributes or directives are
+ignored. This instruction only operates during the expand phase of the JAR. It has not further semantics.
The primary motivation for this instruction is the use of the @Export annotation. Using that annoation puts
+bnd in a bind. A Private-Package should be private and an Export-Package should be exported. Since a bnd file
+should always have the final say, the @Export instruction needed an instruction that took in the packages but
+that did not put export semantics on it.
This instruction is also taken into account when taken the decision to automatically fill up the JAR from the +current project. If this instruction is set (or any other construction instruction) it is assumed that the user +wants to be in control and the instructions are followed. If none of these instructions is set then the JAR is +constructed from the project's output folder.
+When the packages selected by the -includepackage instructions overlap with either Private-Package or Export-Package
+then the Export-Package has the highest priority and then Private-Package. That is, any @Export in a package that is selected
+by Export-Package or Private-Package is ignored silently.
Notice that an annotation like @Version in a a package like selected can still provide the version if the Export-Package
+header for that package provides no version.
The following example includes all packages from com.example.* except com.example.bar.
-includepackage !com.example.bar, com.example.*
+Example: -includeresource: lib/=jar/, {preprocess.txt}, 'literal';literal;=true,
Pattern: .*
The purpose of -includeresource is to fill the JAR with non-class resources. In general these come from the file system. For example, today it is very common to have these type of resources in src/main/resources. This pattern can easily be simulated by bnd with the -includeresource instruction. However, since in OSGi the packaging is so important the -includeresource contains a number of options to minimize files on disk and speed up things.
The syntax of the -includeresource has become quite complex over time:
-includeresource ::= iclause ( ',' iclause ) *
+iclause ::= (unroll | copy) parameter*
+copy ::= '{' process '}' | process
+process ::= assignment | source
+assignment ::= PATH '=' source
+source ::= ('-')? PATH parameter*
+unroll ::= '@' (PATH | URL) ( '!/' SELECTOR )?
+parameters ::= 'flatten:' | 'recursive:' | 'filter:' | `-preprocessmatchers`
+In the case of assignment or source, the PATH parameter can point to a file or directory. It is also possible to use the name.ext path of a JAR file on the classpath, that is, ignoring the directory. The source form will place the resource in the target JAR with only the file name, therefore without any path components. That is, including src/a/b.c will result in a resource b.c in the root of the target JAR.
If the PATH points to a directory, the directory name itself is not used in the target JAR path. If the resource must be placed in a sub directory of the target jar, use the assignment form. If the file is not found, bnd will traverse the classpath to see of any entry on the classpath matches the given file name (without the directory) and use that when it matches. The inline requires a ZIP or JAR file, which will be completely expanded in the target JAR (except the manifest), unless followed with a file specification. The file specification can be a specific file in the jar or a directory followed by or *. The indicates recursively and the indicates one level. If just a directory name is given, it will mean *.
The filter: directive is an optional filter on the resources. This uses the same format as the instructions. Only the file name is verified against this instruction.
Include-Resource: @osgi.jar,[=\ =]
+ {LICENSE.txt},[=\ =]
+ acme/Merge.class=src/acme/Merge.class
+The -includeresources instruction will be merged with all properties that starts with -includeresources*.
A clause contained in curly braces ({ }) are preprocessed. While copying the files are run through the macro processor with the builder providing the properties. In the workspace model, all macros of the project are then available. Well known binary resources (as decided by their extension) are ignored. You can override the extension list with the -preprocessmatchers instruction. This must be a a selector that takes the source file name as the input. The clause can also specify a local -preprocessmatchers. This selector is prepended to the either the default pre process matchers or the set pre process matchers. This allows for the selection or rejection of specific files and/or extensions.
-includeresource: {src/main/resources}, {legal=contracts}
+A source in the clause starting with a - sign will not generare an error when the source in the clause cannot be located. This is very convenient if you specify an global -includeresource instruction in build.bnd. For example, -includeresource.all = -src/main/resources will not complain when a project does not have a src/main/resources directory. Note that the minus sign must be on the source. E.g.
`-includeresource.all = {foo=-bar}`, -foo.txt
+There are two variants of the rolling operator @. It can be used to roll up a directory as a zip or jar file, or it can be used to unroll a jar file into its constituents.
If the destination is a path of a jar or zip file, like foo/bar/icons.zip and the source points to a directory in the file system, then the directory will be wrapped up in a Jar and stored as a single entry in the receiving jar file.
-includeresource foo/bar/icons.zip=@icons/
+Unrolling is getting the content from another JAR. It is activated by starting the source with an at sign (@). The at sign signals that it is not the actual file that should be copied, but the contents of that file should be placed in the destination.
-includeresource tmp=@jar/foo.jar
+The part that follows the at sign (@) is either a file path or a URL. Without any extra parameters it will copy all resources except the ones in the -donotcopy list and the META-INF/MANIFEST.
-includeresource @jar/foo.jar
+This is an ideal way to wrap a bundle since it is a full copy. After that one can add additional resources or use -exportcontents to export the contained packages in the normal way. In this way, bnd will calculate all imports.
The unrolling can also be restricted with a single selector. The syntax for the selector must start with a !/ marker, which is commonly used for this purpose. After the !/ the normal selector operators and patterns can be used. For example, if we want to get just the LICENSE from a bundle then we can do:
-includeresource @jar/foo.jar!/LICENSE
+However, since selectors can also negate, it is also possible to do the reverse:
+-includeresource "@jar/foo.jar!/!LICENSE"
+This is a single selector, it is therefore not possible to specify a chain with rejections and selections. However, also a single selector can match multiple file paths:
+-includeresource @jar/osgi.jar!/!(LICENSE|about.html|org/*)
+Wrapping often requires access to a JAR from the repository. It is therefore common to see the unrolling feature being combined with the ${repo} macro. The ${repo} macro is given the Bundle Symbolic Name and an optional version range returns the path to a file from the repository.
-includeresource @${repo;biz.aQute.bndlib}!/about.html
+flatten:=BOOLEAN - puts all files in the file-tree into one folder
-includeresource new.package/=@jar/file.jar!/META-INF/services/*;flatten:=true
+rename:=RENAME - maps the path using a given renaming instruction. Paths are filtered by the given instruction-SELECTOR. The instruction-Selector is compiled to a regex-pattern. This pattern is used to generate a matcher by using the filtered path and the matcher is used to replaceAll using the given extra value.
-includeresource new.package=@jar/cxf-rt-rs-sse-3.2.5.jar!/(META-INF)/(c*f)/(*);rename:=$2/$1/$3.copy
+onduplicate - controls duplicate file handling for files with the same path and filename. See Handling duplicates below.
When unrolling multiple jar files into your target jar then duplicates can occur when multiple files share the same path and filename. By default duplicates overwrite existing files (last wins).
+With the onduplicate directive you can control this behavior. For example there is the command onduplicate:=MERGE which by default is able to merge (append) services files in /META-INF/services/.
Examples:
+onduplicate:=OVERWRITE - (default) duplicates overwrite existing files (in other words: last wins)onduplicate:=MERGE - tries to merge duplicate files under /META-INF/services/ by default. Other paths are skipped.onduplicate:='MERGE,metainfservices' - same as MERGE. metainfservices is a tag which pulls in Plugins with this tag. Currently there is one default Plugin for handling files under /META-INF/services/onduplicate:='sometag' - tries to merge with Plugins tagged with sometagonduplicate:=SKIP - duplicates are skipped (in other words: first wins)onduplicate:=WARN - output a warning if there are duplicatesonduplicate:=ERROR output an error if there are duplicatesWARN and ERROR can be combined with other commands, while OVERWRITE, MERGE, SKIP are mutually exclusive.
+So combinations are possible, e.g.
onduplicate:=WARN,MERGE - this outputs are warning if duplicates occur, but also tries to merge files under META-INF/services.Let's take Apache FOP as an example. This library comes with 4 jars which are not OSGi bundles.
+To use them in OSGi you could wrap them with bnd. Because of some classloading issues related to the ServiceLoader mechanism, one way to do it is to combine them into a single bundle.
+One challenge is its extension mechanism which uses ServiceLoaders under /META-INF/services/.
For example the bundle fop-core contains a file META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader with the content:
org.apache.fop.image.loader.batik.PreloaderWMF
+org.apache.fop.image.loader.batik.PreloaderSVG
+xmlgraphics-commons contains the same file META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader with content:
org.apache.xmlgraphics.image.loader.impl.PreloaderTIFF
+org.apache.xmlgraphics.image.loader.impl.PreloaderGIF
+org.apache.xmlgraphics.image.loader.impl.PreloaderJPEG
+org.apache.xmlgraphics.image.loader.impl.PreloaderBMP
+org.apache.xmlgraphics.image.loader.impl.PreloaderEMF
+org.apache.xmlgraphics.image.loader.impl.PreloaderEPS
+org.apache.xmlgraphics.image.loader.impl.imageio.PreloaderImageIO
+org.apache.xmlgraphics.image.loader.impl.PreloaderRawPNG
+If you combine these two jars into a single target jar you want to ensure that both files do not overwrite each other but are merged / appended instead, in order to be a valid ServiceLoader file.
+This can be achieved by the following instructions:
+@${repo;org.apache.xmlgraphics:fop-core;latest}!/*,\
+@${repo;org.apache.xmlgraphics:xmlgraphics-commons;latest}!/*;onduplicate:=MERGE,\
+The instructions above can be read like this:
+fop-core can be considered the parent which is unrolled without any special handling.onduplicate:=MERGE directive tells bnd to try merging files. By default bnd is only able to merge files under META-INF/services. So bnd will append the duplicate file to the existing file with a line break.For testing purposes it is often necessary to have tiny resources in the bundle. These could of course be placed on the file system but bnd can also generate these on the fly. Since these are defined in the bnd files, the content has full access to the macros. This is done by specifying a literal attribute on the clause.
-includeresource foo.txt;literal='This is some content with a macro ${sum;1,2,3}'
+The previous example will create a resource with the given content.
+When a directory is specified bnd will by default recurse the source and create a similar hierarchy on the destination.
+The recursion and the hierarchy can be controlled with directives.
+-includeresource target/=hierarchy/
+The recursive: directive can be used to indicate that the source should not be recursively traversed by specifying false:
-includeresource target/=hierarchy/;recursive:=false
+In this case, only the hierarchy directory itself will be copied to the target directory. The flatten: directive indicates that if the directories are recursively searched, the output must not create any directories. That is all resources are flattened in the output directory.
-includeresource target/=hierarchy/;flatten:=true
+| Instruction | +Explanation | +
|---|---|
-includeresource: lib/fancylibrary-3.12.0.jar |
+Copy lib/fancylibrary-3.12.0.jar file into the root of the target JAR | +
-includeresource.resources: -src/main/resources |
+Copy folder src/main/resources contents (including subdfolders) into root of the target JAR The arbitrarily named suffix .resources prevents this includeresource directive to be overwritten The preceding minus sign instructs to supress an error for non-existing folder src/main/resources |
+
-includeresource: ${workspace}/LICENSE, {readme.md} |
+Copy the LICENSE file residing in the bnd workspace folder (above the project directory) as well as the pre-processed readme.md file (allowing for e.g. variable substitution) in the project folder into the target JAR | +
-includeresource: ${repo;com.acme:foo;latest} |
+Copy the com.acme.foo bundle JAR in highest version number found in the bnd workspace repository into the root of the target JAR | +
| Instruction | +Explanation | +
|---|---|
-includeresource: images/=img/ or -includeresource: images=img |
+Copy contents of img/ folder (including subdfolders) into an images folder of the target JAR | +
-includeresource: x=a/c/c.txt |
+Copy a/c/c.txt into file x in the root folder of the target JAR | +
-includeresource: x/=a/c/c.txt |
+Copy a/c/c.txt into file x/c.txt in the root folder of the target JAR | +
-includeresource: libraries/fancylibrary.jar=lib/fancylibrary-3.12.jar; lib:=true |
+Copy lib/fancylibrary-3.1.2.jar from project into libraries folder of the target JAR, and place it on the Bundle-Classpath (BCP). It will make sure the BCP starts with '.' and then each include resource that is included will be added to the BCP | +
-includeresource: lib/; lib:=true |
+Copy every JAR file underneath lib in a relative position under the root folder of the target JAR, and add each library to the bundle classpath | +
-includeresource: acme-foo-snap.jar=${repo;com.acme:foo;snapshot} |
+Copy the highest snapshot version of com.acme.foo found in the bnd workspace repository as acme-foo-snap.jar into the root of the target JAR | +
-includeresource: foo.txt;literal='foo bar' |
+Create a file named foo.txt containing the string literal "foo bar" in the root folder of the target JAR | +
-includeresource: bsn.txt;literal='${bsn}' |
+Create a file named bsn.txt containing the bundle symbolic name (bsn) of this project in the root folder of the target JAR | +
-includeresource: libraries/=lib/;filter:=fancylibrary-*.jar;recursive:=false;lib:=true or -includeresource: libraries/=lib/fancylibrary-*.jar;lib:=true (as of bndtools 4.2) |
+Copy a wildcarded library from lib/ into libraries and add it to the bundle classpath | +
| Instruction | +Explanation | +
|---|---|
-includeresource: @lib/fancylibrary-3.12.jar!/* |
+Extract the contents of lib/fancylibrary-3.12.jar into the root folder of the target JAR, preserving relative paths | +
-includeresource: @${repo;com.acme.foo;latest}!/!META-INF/* |
+Extract the contents of the highest found com.acme.foo version in the bnd workspace repository into the root folder of the target JAR, preserving relative paths, excluding the META-INF/ folder | +
Example: -init: ${my_macro}
Pattern: .*
The -init instruction specifies one or more macros to execute when initializing the project for building. This ensures that certain setup steps are always performed before the build starts.
Example:
+-init: ${my_macro}, ${my_macro2}
+Macros are usually resolved and executed on demand, but those listed in -init are always executed during project initialization.
TODO Needs review - AI Generated content
+Example: -invalidfilenames:CON(\..+)?|PRN(\..+)?|AUX(\..+)?|CLOCK\$|NUL(\..+)?|COM[1-9](\..+)?|LPT[1-9](\..+)?|\$Mft|\$MftMirr|\$LogFile|\$Volume|\$AttrDef|\$Bitmap|\$Boot|\$BadClus|\$Secure|\$Upcase|\$Extend|\$Quota|\$ObjId|\$Reparse
Pattern: .*
The -invalidfilenames instruction allows you to specify file or directory names that should not be used in your project because they are not portable across all operating systems (especially Windows). By default, bnd checks for reserved names that are problematic on Windows, such as CON, PRN, AUX, NUL, COM1, LPT1, and others.
You can customize the regular expression used to detect invalid names by setting this instruction. If any files or directories in your JAR match the specified pattern, bnd will report an error, helping you avoid portability issues when distributing your bundles.
+TODO Needs review - AI Generated content
+Example: -javaagent: true
Values: true,false
Pattern: true|false|TRUE|FALSE
If the value is true, then each classpath elements that
+has a Premain-Class manifest header is used as a java agent
+when launching by adding a -javaagent: argument to the java invocation.
-javaagent:jarpath
+If the classpath element was specified with an agent attribute, the
+value of the agent attribute will be used as the options for the
+-jaragent: argument to the java invocation.
-javaagent:jarpath=options
+Example: -jpms-module-info: foo.module;version=5.4.1; access="OPEN,SYNTHETIC"
Pattern: .*
See jpms for an overview and the detailed rules how the module-info.class file is
+calculated.
The -jpms-module-info instruction is a single parameter
-jpms-module-info ::= module-name [ ';version=' VERSION ] access
+access ::= `;access=' '"' item ( ',' item ) * '"'
+item ::= 'OPEN' | 'SYNTHETIC' | 'MANDATED'
+Automatic-Module-Name is used, or if that one is not set, the Bundle-SymbolicName.version – The version, otherwise the bundle version is used.access – The access flags. These indicate the access mode of the module.For example:
+-jpms-module-info: foo.module;version=5.4.1; access="OPEN,SYNTHETIC"
+Example: -jpms-module-info-options: java.enterprise;transitive="true"
Pattern: .*
See jpms for an overview and the detailed rules how the module-info.class file is
+calculated.
The -jpms-module-info-options instruction provides some capabilities to help the developer handle these scenarios. The keys of these instructions are module names and there are 4 available attributes.
-jpms-module-info-options ::= moduleinfo
+moduleinfo ::= NAME
+ [ ';substitute=' substitute ]
+ [ ';ignore=' ignore ]
+ [ ';static=' static ]
+ [ ';transitive=' transitive ]
+They attributes are:
+substitute - If bnd generates a module name based on the file name and it matches the value of this attribute it should be substituted with the key of the instruction.
+ e.g.
-jpms-module-info-options: java.enterprise;substitute="geronimo-jcdi_2.0_spec"
+means that if bnd calculates the module name to be geronimo-jcdi_2.0_spec it should replace it with java.enterprise
– ignore - If the attribute ignore="true" is found the require matching the key of the instruction will not be added.
+ e.g.
properties
+ -jpms-module-info-options: java.enterprise;ignore="true"
means ignore the module java.enterprise
static - If the attribute static="true|false" is found the access of the module matching the key of the instruction will be set to match.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;static="true"
means make the require for module java.enterprise static
transitive - If the attribute transitive="true|false" is found the access of the module matching the key of the instruction will be set to match.
+ e.g. properties
+ -jpms-module-info-options: java.enterprise;transitive="true"
means make the require for module java.enterprise transitive
The following is an example with multiple attributes and instructions:
+-jpms-module-info-options: \
+ java.enterprise;substitute="geronimo-jcdi_2.0_spec";static=true;transitive=true,\
+ java.management;ignore=true;
+Example: -jpms-multi-release: true
Values: true,false
Pattern: true|false|TRUE|FALSE
This instruction controls that if a JAR setup to be a multi-release jar the manifests & module-infos
+for each supported versions should be added. If this instruction is true, it will generate this metadata,
+if the instruction is absent or the value is not true, then it will ignore the versions.
A multi release Jar (MRJ) will contain directories in META-INF/versions/, where the directory name is a release
+number. If this instruction is enabled, then during manifest generation, bnd will also calculate a manifest and
+module-info in each versioned directory.
Example: -launcher manage = all
Pattern: .*
This instruction has as purpose to collect options and special settings for the launcher. The +following options are architected:
+manage – Indicates to the launcher how to treat unrecognized bundles. When the launcher starts
+ it gets a list of run bundles, also called its scope. However, previous runs could've installed
+ other bundles that do not occur in the scope. By default the launcher should manage all
+ bundles. However, sometimes these bundles were installed by an agent and the launcher should
+ not touch them. Therefore the values for the 'manage' part are:all – This is the default and the launcher assumes it owns all the bundlesnarrow – The launcher will only touch the bundles that are part of its scopenone – The launcher should defer from managing any bundles. -launcher manage = all
+This instruction was primarily designed to handle start levels. Originally the launcher was +narrowly managing only the bundles that were in its scope. However, this was inadvertently +changed and not discovered for several reasons. The option to narrowly manage was therefore +introduced with the default being the latest behavior.
+Example: -library: foo;version=1.2.3
Pattern: .*
The main reason for the -library instruction is to let you package and share bnd-related configuration in a self-contained way so it can be reused across workspaces, projects, or run descriptions.
Let's use some examples for explanation.
+You have many projects that all need the same baseline OSGi or bnd settings—like default macros, a specific build plugin, or a certain analyzer configuration (e.g., “treat warnings as errors,” “export packages in a particular way,” etc.).
+project.bnd (or workspace.bnd/bndrun.bnd) inside a “library bundle” in your repo. -library myconfigThis avoids copying the same lines into each bnd.bnd. Instead, every project references the same library, ensuring consistency.
You have a plugin (e.g., for code generation) that always needs a certain set of properties to work properly—maybe a specific source folder or some environment variables.
+bnd file with the plugin’s configuration. -library codegenThis is particularly nice if you have multiple plugins or complex plugin settings that you do not want to replicate.
+A library is stored in a bundle. A bundle can contain multiple libraries that are each described by a
+capability in the bnd.library name space. This capability looks like:
Provide-Capability: \
+ bnd.library; \
+ bnd.library = foo; \
+ version = 1.2.3; \
+ path = lib/foo
+The following attributes are defined for the bnd.library capabilities:
bnd.library – The name of the library. Names should be kept simple but the names are shared between all libraries. version – The version of the library.path – A path to a directory in the bundle. The contents of this directory are the root of the library. This root
+ is copied to the workspace cache.The root of the library should contain the bnd files to be included. The defaults (workspace.bnd, project.bnd, and
+bndrun.bnd) should only be there if it makes sense to include the library in that type.
A library can provide additional named functionality to a workspace, a project, or a bndrun file. It does this by
+including a bnd file in the setup that originates from a bundle in the repositories. This included bnd file
+can refer and use any binary resources in this bundle. Bundles can contain multiple libraries.
The -library instruction can apply a library extension from the repositories to a workspace or project. A library
+extension is a resource that is stored in one of the repositories. When the -library instruction is used, the corresponding resource
+will be expanded in the workspace's cache and one or more files from this area are read as include files containing
+bnd properties.
-library ::= library ( ',' library )*
+library ::= '-'? NAME ( ';' parameter ) *
+For example, the library foo is included in a resource in the repository. We can apply this library in the
+build.bnd, bnd.bnd, or *.bndrun file.
-library foo
+The following parameter attributes are architected:
* version – The version range of the capability. If no version is specified, 0 is used. If a version is specified, then
+ this is the lowest acceptable version. The runtime will select the highest matching version. The version can also be:
+ * file – the library name is a path to a directory or JAR file. Since this lacks the 'where' of the library in the
+ directory or JAR, this can be set with the where attribute in the same clause.
+* include – The name of the include file or directory, relative to the root directory of the library. If a directory
+ is targeted, all *.bnd files will be read. The default include depends on where the library is included. In:
+ * The workspace (build.bnd) – workspace.bnd
+ * A project (bnd.bnd) – project.bnd
+ * A bndrun spec (*.bndrun) – bndrun.bnd
Some remarks:
+-, there will be no error if the capability cannot be found.${.} macro refers to the cached root directory of the library.Since libraries are stored in repositories but can also provide new repositories it is important to +understand the ordering.
+Only libraries included in the workspace can contribute repositories. The workspace will first read the cnf/ext
+directory bnd files and then build.bnd. Any repositories defined in these bnd files can be used for libraries.
+The workspace will include the libraries based on these repositories. However, after all libraries have
+been included, all plugins are reset and this will reset the repositories. Any repository plugins defined
+in a library will then become available.
Projects and bndrun files can include libraries but they cannot define any new repositories.
+NOTE: what about standalone bndruns?
+Example: -make: (*).jar;type=bnd; recipe="bnd/$1.bnd"
Pattern: .*
type Type name for plugin.Example: type=bnd
Values: bnd
Pattern: .*
recipe Recipe for the plugin, can use back references.
Example: recipe="bnd/$1.bnd"
Values: bnd
Pattern: .*
package aQute.bnd.make;
+
+ import java.util.*;
+ import java.util.Map.Entry;
+ import java.util.regex.*;
+
+ import aQute.bnd.header.*;
+ import aQute.bnd.osgi.*;
+ import aQute.bnd.service.*;
+
+ public class Make {
+ Builder builder;
+ Map<Instruction,Map<String,String>> make;
+
+ public Make(Builder builder) {
+ this.builder = builder;
+ }
+
+ public Resource process(String source) {
+ Map<Instruction,Map<String,String>> make = getMakeHeader();
+ builder.trace("make " + source);
+
+ for (Map.Entry<Instruction,Map<String,String>> entry : make.entrySet()) {
+ Instruction instr = entry.getKey();
+ Matcher m = instr.getMatcher(source);
+ if (m.matches() || instr.isNegated()) {
+ Map<String,String> arguments = replace(m, entry.getValue());
+ List<MakePlugin> plugins = builder.getPlugins(MakePlugin.class);
+ for (MakePlugin plugin : plugins) {
+ try {
+ Resource resource = plugin.make(builder, source, arguments);
+ if (resource != null) {
+ builder.trace("Made " + source + " from args " + arguments + " with " + plugin);
+ return resource;
+ }
+ }
+ catch (Exception e) {
+ builder.error("Plugin " + plugin + " generates error when use in making " + source
+ + " with args " + arguments, e);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private Map<String,String> replace(Matcher m, Map<String,String> value) {
+ Map<String,String> newArgs = Processor.newMap();
+ for (Map.Entry<String,String> entry : value.entrySet()) {
+ String s = entry.getValue();
+ s = replace(m, s);
+ newArgs.put(entry.getKey(), s);
+ }
+ return newArgs;
+ }
+
+ String replace(Matcher m, CharSequence s) {
+ StringBuilder sb = new StringBuilder();
+ int max = '0' + m.groupCount() + 1;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '$' && i < s.length() - 1) {
+ c = s.charAt(++i);
+ if (c >= '0' && c <= max) {
+ int index = c - '0';
+ String replacement = m.group(index);
+ if (replacement != null)
+ sb.append(replacement);
+ } else {
+ if (c == '$')
+ i++;
+ sb.append(c);
+ }
+ } else
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ Map<Instruction,Map<String,String>> getMakeHeader() {
+ if (make != null)
+ return make;
+ make = Processor.newMap();
+
+ String s = builder.getProperty(Builder.MAKE);
+ Parameters make = builder.parseHeader(s);
+
+ for (Entry<String,Attrs> entry : make.entrySet()) {
+ String pattern = Processor.removeDuplicateMarker(entry.getKey());
+
+ Instruction instr = new Instruction(pattern);
+ this.make.put(instr, entry.getValue());
+ }
+
+ return this.make;
+ }
+ }
+Example: -manifest=META-INF/MANIFEST.MF
Pattern: .*
The -manifest instruction allows you to override the default manifest calculation and specify a fixed manifest file to use in your JAR. When this instruction is set, bnd will use the provided manifest file instead of generating one automatically, although it will still analyze the classpath as part of the build process.
This is useful when you need to comply with specific manifest requirements or reuse an existing manifest file for your bundle.
+TODO Needs review - AI Generated content
+.*The -manifest-name instruction allows you to set a custom resource path for the manifest file in your JAR. By default, the manifest is stored as META-INF/MANIFEST.MF, but some standards or environments may require a different name or location.
Use this instruction to override the default manifest path when needed. For example, you might set it to a different file name or directory to comply with a specific packaging requirement.
+TODO Needs review - AI Generated content
+.*The -pom instruction can be used to generate a pom in the bundle. The value of the -maven-dependencies instruction is used to generate the <dependencies> section in the generated pom.
-maven-dependencies ::= entry ( ',' entry ) *
+ entry ::= key ( ';' attribute ) *
+ attribute ::= groupId-attr | artifactId-attr | version-attr
+ | classifier-attr
+ groupId-attr ::= 'groupId' '=' groupId
+ artifactId-attr ::= 'artifactId' '=' artifactId
+ version-attr ::= 'version' '=' version
+ classifier-attr ::= 'classifier' '=' classifier
+ gavkey ::= groupId ':' artifactId ':' version ( ':jar:' classifier)?
+For Bnd Workspace builds, the -maven-dependencies instruction is automatically set, if not already set, by Bnd from the -buildpath entries of the project. Normally, you can allow Bnd to automatically set the -maven-dependencies instruction. But you can override the maven dependency information by explicitly setting the -maven-dependencies instruction. If set to the empty string, then no <dependencies> section will be added in the generated pom. Since the -maven-dependencies instruction is a merged instruction, you can use suffixes to override the generated information for a dependency and to add additional dependencies to the maven dependency information.
When Bnd automatically sets the -maven-dependencies instruction, it will generate the key value using the gavkey production with the groupId, artifactId, version, and classifier (if set) values of the artifact. Using a well defined gavkey value, allows the attributes of a specific artifact to be overriden by merged property value while allowing the other automatically set values to still be used. However any unique key value can be used if you don't care to override the automatically set value for a specific dependency.
When an artifact come from a maven repository, such as MavenBndRepository or BndPomRepository, the maven repository will supply the groupId, artifactId, version, and classifier (if the artifact has a non-empty classifier) attributes of the artifact. If the artifact does not come from a maven repository but does contain a pom.properties resource, then the groupId, artifactId, and version attributes are supplied by that resource.
An attribute value can also be specified on a -buildpath entry, by prefixing the attribute name with maven-, which can then replace any such attribute value coming from the maven repository or a pom.properties resource.
You can set the maven-scope attribute on a -buildpath entry to specify a different dependency scope than the default dependency scope specified by the -maven-scope instruction. The value of the maven-scope attribute must be a valid dependency scope.
You can also set the maven-optional attribute on a -buildpath entry to true which will exclude the artifact from the generated dependencies information. The default value for maven-optional is false.
Disable generating the <dependencies> section in the generated pom.
-maven-dependencies:
+Override the automatically set dependency information for a specific artifact. You must use the matching gavkey of the actual artifact and then specify the groupId, artifactId, and version attributes to use for the artitact. This example replaces the version of the artifact to remove -SNAPSHOT.
-maven-dependencies.fix: org.osgi:osgi.annotation:8.0.0-SNAPSHOT;\
+ groupId=org.osgi;artifactId=osgi.annotation;version=8.0.0
+This could also be done on the -buildpath entry for the artifact by specifying the maven-version attribute to override the maven version of the artifact.
-buildpath: osgi.annotation;version=8.0.0.SNAPSHOT;maven-version=8.0.0
+Add an additional dependency. You must specify the groupId, artifactId, and version attributes to use for the artitact.
-maven-dependencies.log: log;groupId=org.osgi;\
+ artifactId=org.osgi.service.log;version=1.5.0
+Don't generate a <dependency> element for a specific artifact. This is done by specifying the proper gavkey value and not specifying any attributes.
-maven-dependencies.nodependency: org.osgi:osgi.annotation:8.0.0
+This could also be done on the -buildpath entry for the artifact by specifying the maven-optional attribute with the value true.
-buildpath: osgi.annotation;version=8.0.0;maven-optional=true
+Change the dependency scope for the artifact. The default dependency scope is specified by the -maven-scope instruction.
+-buildpath: osgi.annotation;version=8.0.0;maven-scope=provided
+Example: -maven-release: local
Values: (local|remote)
Pattern: .*
The -maven-release instruction provides the context for a release to Maven repository. In the Maven world it is customary that a release has a JAR with sources and a JAR with Javadoc. In the OSGi world this is unnecessary because the sources can be packaged inside the bundle. (Since the source is placed at a standard location, the IDEs can take advantage of this.) However, putting an artifact on Maven Central requires that these extra JARs are included. This instruction allows you to specify additional parameters for this release process.
Though this instruction is not specific for a plugin, it was developed in conjunction with the Maven Bnd Repository Plugin.
+-maven-release ::= ( 'local'|'remote' ( ';' snapshot )? ) ( ',' option )*
+snapshot ::= <value to be used for timestamp>
+option ::= sources | javadoc | pom | sign | archive*
+archive ::= 'archive'
+ ( ';path=' ( PATH | '{' PATH '}' )?
+ ( ';classifier=' maven-classifier )?
+sources ::= 'sources'
+ ( ';path=' ( 'NONE' | PATH ) )?
+ ( ';force=' ( 'true' | 'false' ) )?
+ ( ';-sourcepath=' PATH ( ',' PATH )* )?
+javadoc ::= 'javadoc'
+ ( ';path=' ( 'NONE' | PATH ) )?
+ ( ';packages=' ( 'EXPORTS' | 'ALL' ) )?
+ ( ';force=' ( 'true' | 'false' ) )?
+ ( ';' javadoc-option )*
+javadoc-option ::= '-' NAME '=' VALUE
+pom ::= 'pom'
+ ( ';path=' ( 'JAR' | PATH ) )?
+sign ::= 'sign'
+ ( ';passphrase=' VALUE )?
+ ( ';keyname=' VALUE )?
+If sources or javadoc has the attribute force=true, either one will be release to the maven repository even if no releaseUrl or snapshotUrl is set or maven-release=local.
The aQute.maven.bnd.MavenBndRepository is a bnd plugin that represent the local and a remote Maven repository. The locations of both repositories can be configured. The local repository is always used as a cache for the remote repository.
For a detailed configuration of the Maven Bnd Repository Plugin, please look at the documentation page.
+If the Maven Bnd Repository is asked to put a file, it will look up the -maven-release instruction using merged properties. The property is looked up from the bnd file that built the artifact. However, it should in general be possible to define this header in the workspace using macros like ${project} to specify relative paths.
The archive option provides a way to add additional files/archives to release. A Maven release always has a pom and then a number of files/archives that are separated by a classifier. The default classifier is generally the jar file. Special classifiers are reserved for the sources and the javadoc.
The archive option takes the following parameters:
path : The path to the file that will be placed in the release directory. If the path is surrounded by curly braces, it will be pre-processed.classifier : The classifier of the file. This is the maven classifier used.For example:
+ -maven-release \
+ archive;\
+ path=files/feature.json;
+ classifier=feature
+If the instruction contains the sign attribute and release build is detected the repository tries to apply gnupg via a command process to create .asc files for all deployed artifacts. This requires a Version of gnupg installed on your build system. By default it uses the gpg command. If the passphrase is configured, it will hand it over to the command as standard input, same for the keyname.
The command will be constructed as follows:
+gpg --batch --local-user <keyname> --passphrase-fd 0 --output <filetosign>.asc --detach-sign --armor <filetosign>. Some newer gnupg versions will ignore the passphrase via standard input for the first try and ask again with password screen. This will crash the process. Have a look here to teach gnupg otherwise. The command can be exchanged or amended with additional options by defining a property named gpg in your workspace (e.g. build.bnd or somewhere in the ext directory).
Example config could look like:
+# use the env macro to avoid to set the passphrase somehwere in your project
+-maven-release: pom,sign;keyname=${env;GNUPG_KEYNAME}, passphrase=${env;GNUPG_PASSPHRASE}
+gpg: gpg --homedir /mnt/n/tmp/gpg/.gnupg --pinentry-mode loopback
+Example: -maven-scope: compile
Values: (compile|provided)
Pattern: .*
The -pom instruction can be used to generate a pom in the bundle. The -maven-scope instruction can be used to specify the default dependency scope to use when Bnd generates maven dependency information for a -buildpath entry that will be used to create a <dependency> in the generated pom.
Also see the -maven_dependencies instruction for information on how to manualy configure the maven dependency information in a generated pom.
+-maven-scope defaults to the value compile which is the default dependency scope for Maven.
Change the default dependency scope to provided.
-maven-scope: provided
+Example: -metainf-services: auto
Values: (annotation|auto|none)
Pattern: auto|annotation|none
The -metainf-services instruction tells bnd how files in the META-INF/services should be processed.
+See the chapter about META-INF/services Annotations how to apply Bundle annotations to the files in the META-INF/services.
This instruction can have the following values:
+annotation (default if not set): Scan META-INF/services files and only process those files which contain at least one textual annotations.auto: The convenience strategy. Scans META-INF/services files and auto-registers implementations in services files which do not have a single textual annotation. The latter means that bnd behaves as if there was a aQute.bnd.annotation.spi.ServiceProvider annotation present on each implementation. This is useful if you just want to have bnd generate the Provide-Capability headers for osgi.serviceloader. Additionally auto behaves like annotation and would process all other files with textual annotations.none: skip processing of files in META-INF/services completelyAssume we want to wrap a non-OSGi library containing META-INF/services.
+Create bnd.bnd file and put a library having a META-INF/services folder e.g. groovy-3.0.22.jar in a local lib folder.
+We will use -includeresource: @lib/groovy-3.0.22.jar to unroll the jar.
# bnd.bnd
+-includeresource: @lib/groovy-3.0.22.jar
+-metainf-services: auto
+This creates a new jar with the following MANIFEST.MF headers from the META-INF/services folder:
# MANIFEST.MF
+Provide-Capability osgi.service;objectClass:List<String>="org.codehaus.groovy.transform.ASTTransformation";effective:=active
+ osgi.serviceloader;osgi.serviceloader="org.codehaus.groovy.transform.ASTTransformation";register:="groovy.grape.GrabAnnotationTransformation"
+Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
+ osgi.extender;filter:="(&(osgi.extender=osgi.serviceloader.registrar)(version>=1.0.0)(!(version>=2.0.0)))";resolution:=optional
+Because -metainf-services: auto is used, it instructs bnd to auto-generate a @ServiceProvider annotation under the hood for services without annotations.
+To prevent the latter (auto-generation) use the default -metainf-services: annotation (to process only textual annotations) or use -metainf-services: none to skip processing of META-INF/services files completely.
Example: -metatypeannotations: *
Pattern: .*
The -metatypeannotations instruction tells bnd which bundle classes, if any, to search for Metatype Service annotations. bnd will then process those classes into Metatype XML resources.
The value of this instruction is a comma delimited list of fully qualified class names.
+The default value of this instruction is *, which means that by default bnd will process all bundle classes looking for Metatype annotations.
The behavior of Metatype annotation processing can be further configured using the -metatypeannotations-options instruction.
+ +Example: -metatypeannotations-options: version;minimum=1.2.0
Values: (inherit|felixExtensions|extender|nocapabilities|norequirements|version)
Pattern: .*
-metatypeannotations-options: version;minimum=1.2.0
+Analogous to -dsannotations-options, this will also restrict the use of OSGi Metatype annotations to minimum 1.2.0 version. The version number denotes that the users can use any version equal to or higher than 1.2.0, provided that the users have the Metatype annotations included on the build path.
Example: -namesection=*;baz=true, abc/def/bar/X.class;bar=3
Pattern: .*
Create a name section (second part of manifest) with optional property expansion and addition of custom attributes.
+The key of the -namesection instruction is an ant style glob. And there are two target groups for matching:
/ or is an exact match for a resource path/ or is an exact match for a package path The goal of named sections is to provide attributes over a specific subset of resources and paths in the jar file. Attributes are specified using the same syntax used elsewhere (such as package attributes). Attributes can contain properties and macros for expansion and replacement.
+Each attribute is processed by bnd and the matching value is passed using the @ property.
Resources are targeted by using a glob pattern not ending with /.
For example, the following instruction sets the content type attribute for png files:
-namesection: com/foo/*.png; Content-Type=image/png
+This produces a result like the following:
+Name: org/foo/icon_12x12.png
+Content-Type: image/png
+
+Name: org/foo/icon_48x48.png
+Content-Type: image/png
+Packages are targeted by using a glob pattern that ends with /.
For example, to produce a Java Package Version Information section use an instruction like this one:
+-namesection: jakarta/annotation/*/;\
+ Specification-Title=Jakarta Annotations;\
+ Specification-Version=${annotation.spec.version};\
+ Specification-Vendor=Eclipse Foundation;\
+ Implementation-Title=jakarta.annotation;\
+ Implementation-Version=${annotation.spec.version}.${annotation.revision};\
+ Implementation-Vendor=Apache Software Foundation
+This produces a result like the following:
+Name: jakarta/annotation/
+Implementation-Title: jakarta.annotation
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: 2.0.0-M1
+Specification-Title: Jakarta Annotations
+Specification-Vendor: Eclipse Foundation
+Specification-Version: 2.0
+Example: -nobuildincache=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nobuildincache instruction controls whether a build cache is used for the launcher and JUnit. When set to true, bnd will not use a build cache, which can be useful for troubleshooting or when you want to ensure that all builds are performed from scratch without relying on cached files.
By default, the build cache is enabled to improve performance by reusing previously built artifacts. Disabling the cache may slow down builds but can help avoid issues related to stale or corrupted cache data.
+TODO Needs review - AI Generated content
+Example: -nobundles=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nobundles instruction tells bnd to skip building the project. This can be useful for projects that only provide resources or configuration, or for disabling builds in certain environments.
Example:
+-nobundles: true
+When this instruction is set to true, bnd will not produce any output JARs for the project.
TODO Needs review - AI Generated content
+Example: -noclassforname=true
Values: true,false
Pattern: true|false|TRUE|FALSE
Normally Bnd will examine the method bodies of classes looking for the instruction sequence:
+ldc(_w) "some.Class"
+invokestatic "java/lang/Class" "forName(Ljava/lang/String;)Ljava/lang/Class;"
+which results from calls to Class.forName(String) passing a String constant for the class name. Bnd will use the String constant as a class reference for the purposes of calculating package references for generating the Import-Package manifest header.
The -noclassforname instruction can be used to tell Bnd to not search the method bodies for this instruction sequence.
For example:
+-noclassforname: true
+Example: -nodefaultversion=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nodefaultversion instruction controls whether a default version is added to exported packages when no version is specified. By default, bnd will add the bundle version as the version for any exported package that does not have an explicit version. When this instruction is set to true, no default version will be added, and exported packages without a version will remain unversioned.
This can be useful if you want to avoid implicit versioning and ensure that only explicitly specified versions are used in your exported packages.
+TODO Needs review - AI Generated content
+Example: -noee=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -noee instruction controls whether bnd automatically adds a requirement for an Execution Environment (EE) capability based on the class file format. By default, bnd will analyze the class version and add the minimum required EE as a requirement. When this instruction is set to true, bnd will not add this automatic requirement, giving you full control over EE requirements in your bundle.
This is useful if you want to manage EE requirements manually or if your project has special compatibility needs.
+TODO Needs review - AI Generated content
+Example: -noextraheaders=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -noextraheaders instruction controls whether bnd adds extra headers to the manifest that are specific to bnd, such as Created-By, Tool, and Bnd-LastModified. By default, these headers are included to provide information about the build environment and tool versions. When this instruction is set to true, bnd will not add these extra headers, resulting in a cleaner manifest with only the standard OSGi headers.
This can be useful if you want to minimize metadata or ensure that your manifest contains only the required information.
+TODO Needs review - AI Generated content
+Example: -noimportjava=true
Values: true,false
Pattern: true|false|TRUE|FALSE
Prior to OSGi Core R7, it was invalid for the Import-Package header to include java.* packages. So Bnd would never include them in the generated Import-Package header. In OSGi Core R7, or later (e.g. OSGi Core R8), it is now permitted to include java.* packages in the Import-Package header. This allows the OSGi framework validate the execution environment can supply all the java packages required by a bundle. This can avoid a NoClassDefFoundError during execution of the bundle due to a missing java.* package required by the bundle.
Bnd will now generate the Import-Package header including referenced java.* packages when either the bundle imports the org.osgi.framework package from OSGi Core R7, or later, or when the bundle includes class files targeting Java 11, or later.
The -noimportjava instruction can be used to tell Bnd not to include referenced java.* packages in the generated Import-Package header.
For example:
+-noimportjava: true
+You can use the Import-Package instruction to control which referenced java.* packages should be imported.
For example:
+Import-Package: java.util.*, !java.*, *
+will only import java.util.* packages and no other java.* packages.
Example: -nojunit=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nojunit instruction indicates that the project does not contain JUnit tests. When this instruction is set to true, bnd will not attempt to run JUnit tests for the project, even if test sources are present.
This is useful for projects that do not require unit testing or do not have any test cases that need to be executed as part of the build process.
+TODO Needs review - AI Generated content
+Example: -nojunitosgi=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nojunitosgi instruction indicates that the project does not contain JUnit OSGi tests. When this instruction is set to true, bnd will not attempt to run OSGi-based JUnit tests for the project. This is useful for projects that do not require OSGi test execution or do not have any test cases that need to be run in an OSGi environment.
By default, if test sources and test cases are present, bnd will attempt to run both standard and OSGi-based JUnit tests unless this instruction is specified.
+TODO Needs review - AI Generated content
+Example: -nomanifest=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -nomanifest instruction controls whether the manifest file is included in the generated JAR. When set to true, the manifest will not be saved in the JAR file. This can be useful in scenarios where a manifest is not required or when you want to minimize the contents of the JAR for specific use cases.
Typically, most OSGi bundles require a manifest, but in some advanced or custom build scenarios, you may wish to omit it. Use this option with caution, as omitting the manifest may cause the resulting JAR to be unusable in standard OSGi environments.
+By default, the manifest is included in the JAR unless -nomanifest: true is specified.
TODO Needs review - AI Generated content
+Example: -noparallel=true
Values: true,false
Pattern: true|false|TRUE|FALSE
Gradle supports --parallel to run build tasks in parallel when possible. This can be a great speed improvement for a build. But sometimes, certain tasks cannot be run in parallel due to certain resource contention.
The -noparallel Bnd instruction can be used to state that any tasks assigned to a category must not be run in parallel with any other task assigned to the same category. The category names are open ended. Any task names specified must be the names of actual Gradle tasks in the project. Multiple categories and multiple task names per category can be specified.
For example:
+-noparallel: launchpad;task="test", port80;task="testOSGi"
+Example: -noproxyinterfaces=true
Values: true,false
Pattern: true|false|TRUE|FALSE
Normally, Bnd examines the method bodies of classes looking for the instruction sequence:
+Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler)
+When Bnd detects a call to Proxy.newProxyInstance, it inspects the Class[] argument — for example, new Class[] { SomeInterface.class, ... } — and treats all listed interfaces as if they were implemented by the class. This means that any types referenced in the parameters or return types of those interfaces must also be imported.
Consider the following code:
+return (ServletContext) Proxy.newProxyInstance(
+ curClassLoader,
+ new Class[] { ServletContext.class },
+ new AdaptorInvocationHandler()
+);
+Bnd will automatically add the package javax.servlet.descriptor to the Import-Package header, because the detected ServletContext interface declares the method JspConfigDescriptor getJspConfigDescriptor().
Interface detection only occurs when the Class[] array is created inline (that is, using the anewarray bytecode pattern shown above).
+If the array is obtained from a field, local variable, or method parameter, Bnd cannot reliably determine its contents from the bytecode, and therefore no interfaces will be detected in those cases.
The -noproxyinterfaces instruction can be used to tell Bnd not to search method bodies for calls to Proxy.newProxyInstance.
For example:
+-noproxyinterfaces: true
+Example: -nosubstitution=true
Values: true,false
Pattern: true|false|TRUE|FALSE
See Subsitution to learn more about package Substition (a key aspect of OSGi allowing that a package can be both exported and imported).
+Note, that package substitution behaviour is enabled by default in bnd for backward-compatibilty. But recommended to disable for most cases these days via -nosubstitution:true.
-nosubstitution:true
+-exportcontents: *
+Import-Package: *
+This ensures that no Import-Package is calculated for any package in Export-Package. This is equivalent to Export-Package: *;-noimport:=true or -exportcontents: *;-noimport:=true.
The advantage is that -nosubstitution:true is a global switch, thus a developer cannot forget to add -noimport:=true in case of a more fine-grained Export-Package declaration with multiple packages.
It is useful for library authors who never want to have any exported packages in Import-Package.
+Often, library authors who are just using bnd to generate OSGi metadata in their build scripts (e.g. via maven or gradle plugins) but are otherwise unfamiliar with bnd and OSGi, need a simple way to disable this substitution behavior, because it can lead to surprising resolution results (or failures) after deployment (because other bundles providing the same package are pulled in). This is often undesirable in a pure "library" use-case.
This instruction helps making bnd / OSGi adoption easier for library projects just wanting to provide OSGi metadata.
+Example: -nouses=true
Values: true,false
Pattern: true|false|TRUE|FALSE
Do not calculate the uses directive on package exports or on capabilities, if set to true. +Default: false
+For example:
+-nouses: true
+Warning: +Setting this flag to true is rarely needed and can be dangerous. Without any uses clause, all packages are treated as independent from each other. That means the OSGi resolver is free to wire these packages to different classloaders if used by a consumer (or its dependencies).
+Example: -output=my_directory
Pattern: .*
The -output instruction allows you to specify the output directory or file for the generated JAR or bundle. You can provide a file path or directory; if a directory is specified, the output file will be named based on the bundle symbolic name and version (e.g., bsn-version.jar).
If no output is specified, bnd will use default naming strategies, such as the name of the source file or a generic name like Untitled. This instruction is useful for controlling where and how your build artifacts are saved.
Note: See also the -outputmask instruction for more advanced output naming options.
TODO Needs review - AI Generated content
+Example: -outputmask=my_file.zip
Pattern: .*
The -outputmask instruction allows you to define a template for naming the output file when building a JAR. You can use any macro in the template, but ${@bsn} and ${@version} are especially useful as they refer to the current bundle symbolic name and version, respectively. The default template is ${@bsn}.jar.
This instruction is helpful for customizing the naming convention of your build artifacts, making it easier to organize and identify them according to your project's needs.
+TODO Needs review - AI Generated content
+Example: -packageinfotype=osgi
Pattern: .*
Example: -pedantic=true
Values: true,false
Pattern: true|false|TRUE|FALSE
When setting this instruction to true there will be more warnings about things that are not really wrong but still not right.
+It can be helpful to fix problems in your workspace.
Note for Eclipse users: Currently a change to this instruction requires a restart of Eclipse. The Refresh-Workspace button is not enough.
+Example: -plugin=aQute.lib.spring.SpringComponent,aQute.lib.deployer.FileRepo;location=${repo}
Pattern: .*
A plugin is a parameterized piece of code that runs inside bndlib. The -plugin instruction defines a plugin by specifying its class name and a given set of parameters; a specific class can be instantiated multiple times.
The -plugin instruction actually aggregates all properties that start with -plugin*. This makes it possible to set plugins in different places, for example include files or with the bndlib workspace extensions. The following sets the Git plugin:
-plugin.git = aQute.bnd.plugin.git.GitPlugin
+Plugins are created at startup using a special plugin class loader. This class loader is pre-loaded with any URLs set in the -pluginpath instructions. The plugin definition can, however, also add additional URLs to this classloader with the 'path:' directive.
If the plugin implements the Plugin interface, it is given the parameters specified in the -plugin instruction. It is then registered in the plugin registry and made available to the rest of the system.
All plugins are (unfortunately) loaded in a single class loader.
+-plugin* ::= plugin-def ( ',' plugin-def )*
+plugin-def ::= qname ( ';' ( attribute | directive ) )*
+The qname must identify a class, if it is an interface then this will load proxies to any external plugins. This class will be loaded with the -pluginpath and/or the path: directive.
Any attributes are passed to the plugin if it implements the aQute.bnd.servce.Plugin interface. Consult the actual plugin for the possible attributes.
The following directives are architected.
+path: – This directive specifies a comma separated list of file paths, the list must be enclosed in quotes when it contains a comma. Each of these files must be a directory or a JAR file and is added to the plugin class loader in the given sequence. command: – If this directive is specified errors on initializing this plugin are only reported if this command is an instruction in the current properties. The purpose of this is to allow plugins to be built in the cnf directory; since the plugin does not exist during its first compilation errors would be reported. Since this project does not use the command itself, it can safely ignore this error.name – Specifies the name of an external plugin. This name is a glob and can this be wildcarded. If not specified the name is *, which will load all external plugins for that type.If the specified qname identifies an interface then the current repositories are searched for external plugins that implement this interface. Any found implementations are turned into a proxy that will lazily load the implementation class. The attributes in the clause will be given as properties when the plugin implements Closeable. External plugins that implement Closeable will be closed as normal plugins.
The following example installs an embedded FileRepo and will load all exporters from this repository.
+-plugin.repo.main:\
+ aQute.lib.deployer.FileRepo; \
+ name='Main'; \
+ location=${build}/repo/main, \
+ aQute.bnd.service.export.Exporter;name=*
+Problem adding path <path> to loader for plugin <key>. Exception: (<e>) – An unexpected exceptio occurred while adding a new path to the plugin class loader.Cannot load the plugin <class-name> – The give plugin cannot be loaded for an unknown reason.Failed to load plugin <class-name>;<attrs>, error: <e> – An exception occurred trying to add a new plugin.While setting properties <properties> on plugin <plugin>, <e> – An exception was thrown by the plugin when receiving its properties.Plugin path: <path>, specified url <url> and a sha1 but the file does not match the sha – The -pluginpath.* instruction specified a download URL and a SHA digest; the file was downloaded but the content did not match the given SHA digest.Plugin path: <path>, specified url <url> and a sha1 '<sha>' but this is not a hexadecimal – The SHA is not in hexadecimal form. No such file <path> from <plugin> and no 'url' attribute on the path so it can be downloaded – The given path in pluginpath* was not found and there was no url attribute specified to download it from.-pluginpath – Specifies JARs that populat the plugin class loader-extensions – An alternative mechanism that can load plugins from repositoriesExample: -pluginpath=${workspace}/cnf/cache/plugins-2.2.0.jar
Pattern: .*
url Specify a URL to download this file from if it does not existExample: url=http://example.com/download/plugins-2.2.0.jar
Pattern: .*
The -pluginpath instruction allows you to define JAR files or directories that should be loaded into the local classloader for plugins. This is useful for plugins that are not embedded in bndlib and need to load their classes from external locations.
You can specify multiple -pluginpath clauses in different places, and they will be merged together. If a specified file does not exist, you can provide a URL to download it, and optionally a SHA-1 digest to verify the download. This ensures that all required plugin dependencies are available and loaded correctly at build time.
TODO Needs review - AI Generated content
+Example: -pom=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -pom instruction can generate a pom derived from the manifest and store it in the
+bundle. The groupId can be specified by the groupid key which defaults to the value of
+the -groupid instruction. If neither the groupid key or the -groupid instruction
+are specified, the groupId will be derived from the Bundle Symbolic Name by using
+everything until the last '.' (bsn prefix) and the artifactId will be everything from the
+last '.' to the end (bsn suffix).
The following properties are supported:
+|Key |Default |Description |
+|groupid |-groupid |The groupId to use. Will default to bsn prefix if no groupid is set.|
+|artifactid |bundle symbolic name|The artifactId to use. Will default to bsn suffix if no groupid is set.|
+|version |bundle version |The version to use. |
+|where |META-INF/maven/<groupid>/<artifactid>/pom.xml|The location of the pom.xml file. Will default to pom.xml if no groupid is set.|
Note: When -pom is defined, then existing META-INF/maven/ subfolders will be removed, before the pom.xml is generated. This might remove folders which existed previously e.g. by unrolling jar files.
The -pom instruction can use any macro but the ${@bsn} and ${@version} macros
+refer to the current JAR being built.
The -pom instruction will also attempt to convert the following headers to their POM counterpart:
Bundle-DescriptionBundle-DocUrlBundle-Vendor – If the value ends with a HTTP or HTTPS url then this URL is used as the organization URL and the name with be the part without the URL. Otherwise the whole value is used as the value for the organization name.Bundle-LicenseBundle-Developers – This is an unofficial header. The key must be the email. It consist of the following parameters:|Parameters |Default |Description|
+|email | |Email address (mandatory)|
+|id |email |A developer id (defaults to email)|
+|name | |Name of the developer|
+|organization | |Name of the organization|
+|organizationUrl | |URL of the organization|
+|roles | |Roles of the developer (comma separated)|
+|timezone | |Three letter time zone|
Bundle-Developers: \
+ Peter.Kriens@aQute.biz; \
+ name="Peter Kriens"; \
+ organization=aQute; \
+ roles="programmer,gopher"
+Bundle-SCM – This is an unofficial header. The key must be the It consists of the following parameters:|Parameters |Default |Description|
+|connection | |Read only connection|
+|developerConnection | |Developer connection|
+|url | |The URL for a web front end to your SCM system.|
Bundle-SCM: \
+ url=https://github.com/bndtools, \
+ connection=scm:git:https://github.com/bndtools/bnd, \
+ developerConnection=scm:git:git@github.com/bndtools/bnd
+The following example bnd file:
+Bundle-SymbolicName: com.example.foo
+Bundle-Version: 1.2.3.qualifier
+-pom: true
+Generates the following pom in pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi=""
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.example</groupId>
+ <artifactId>foo</artifactId>
+ <version>1.2.3.qualifier</version>
+ <name>com.example.foo</name>
+</project>
+You can override the different parts of the Maven coordinates:
+Bundle-SymbolicName: com.example.foo
+Bundle-Version: 1.2.3.qualifier
+Bundle-Developers: Peter.Kriens@aQute.biz; \
+ name="Peter Kriens"; \
+ organization=aQute; \
+ roles="programmer,gopher"
+Bundle-SCM: url=https://github.com/bndtools, \
+ connection=scm:git:https://github.com/bndtools/bnd, \
+ developerConnection=scm:git:git@github.com/bndtools/bnd
+-pom: groupid=com.example, \
+ where=META-INF/maven/pom.xml, \
+ version=${versionmask;==;${Bundle-Version}}
+Generates the following pom in META-INF/maven/pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi=""
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.example</groupId>
+ <artifactId>com.example.foo</artifactId>
+ <version>1.2</version>
+ <name>com.example.foo</name>
+ <scm>
+ <url>https://github.com/bndtools</url>
+ <connection>scm:git:https://github.com/bndtools/bnd</connection>
+ <developerConnection>scm:git:git@github.com/bndtools/bnd</developerConnection>
+ </scm>
+ <developers>
+ <developer>
+ <id>Peter.Kriens@aQute.biz</id>
+ <name>Peter Kriens</name>
+ <organization>aQute</organization>
+ <roles>
+ <role>programmer</role>
+ <role>gopher</role>
+ </roles>
+ <email>Peter.Kriens@aQute.biz</email>
+ </developer>
+ </developers>
+</project>
+.*An typical use case for the -prepare instruction is the generation of CSS files from a less or sccs specification. The prepare instruction will execute a number of commands in the shell before it starts the build process.
The commands used in the shell must work on the platform. Since the shell is so different in a Windows environment this function is not guaranteed to work.
+The following example compiles typescript code in the typescript directory. The output goes to web/repository.js. To install the tsc command, see npm.
-prepare: \
+ web/foo.js <= typescript/*.ts ; \
+ command:=tsc -p typescript --out $@
+-prepare ::= makespec ( ',' makespec )
+makespec ::= dependency ( ';' parameter )+
+dependency ::= PATH ( '<=' FILESPEC )?
+parameter ::= command | report | name | env
+command ::= 'command:=' <shell> # mandatory
+report ::= 'report:=' PATTERN
+name ::= 'name:=' STRING
+env ::= key '=' value
+A makespec defines a dependency in the file system. It must start with a file path, this is the output file of the command. It can be followed with a dependency. A dependency is a FILESPEC. The FILESPEC defines the set of input files to the command. If specified, then the command is only executed when the target file is older than any of the dependencies and the project bnd files.
It is possible to specify a name for the command, this name is then used for any error reporting.
The command parameter specifies the shell command. It must be a valid shell command. Any $@ and $< macros are replaced by the target file path and the set of files in the dependency.
The shell is supposed to support multiple commands (separate with ';') and pipes ('|'). The working directory the command will run in is the project directory.
+Any attributes (i.e. not directives) are added as an environment variable for the command.
+If the executed command provides diagnostic output with file names, line numbers, etc. then it is possible in some cases to report these to the IDE so the error/warning appears on the right place. This requires the specification of a regular expression. This regular expression must specify named groups. A named group is like (?<name>.*). The expression must find error messages from the console output or the error output.
The following names can be used in the regular expression:
+file relative file name
+line zero based line number
+line-1 one based line number
+type `error` | `warning`
+message the message
+For example, the report pattern for the Microsoft TypeScript compiler tsc:
+"(^|\n)(?<file>[^(]+)\\((?<line>[0-9]+),(?<column>[0-9]+)\\):(?<message>[^\n]*)"
+It is possible to set the platforms command search path with the -PATH macro. This macro must contain a comma separated list of PATH, specifying places where to look. If this -PATH macro contains ${@} then this will be replaced with the platforms current PATH. By not specifying the ${@} it is possible to limit the available commands.
Example: -preprocessmatchers=!OSGI-INF/*,*
Pattern: .*
During the -includeresource processing it is possible to pre-process the files that are copied into the JAR by enclosing the clause in curly braces ({}). Since this can create havoc when applied to text files bnd will attempt to skip binary files. To skip binary files, bnd uses a pre-process matchers list. This list is a standard selector. The default is:
!*.(jpg|jpeg|jif|jfif|jp2|jpx|j2k|j2c|fpx|png|gif|swf|doc|pdf|tiff|tif|raw|bmp|ppm|pgm|pbm|pnm|pfm|webp|zip|jar|gz|tar|tgz|exe|com|bin|mp[0-9]|mpeg|mov|):i, *
+When bnd copies a file from a source to a directory it will match that name against this list. If it is one of the extensions listed, then it will not preprocess that file.
+The default can be overridden with the -preprocessmatchers instruction.
-preprocessmatchers: !OSGI-INF/*,*
+Example: -privatepackage: com.example.*, foo.bar
Values: ${packages}
Pattern: .*
The -privatepackage instruction specifies packages to include from the class path as private packages. Unlike the Private-Package header, this instruction is not included in the manifest. It is used to control which packages are bundled privately in the output JAR.
Example:
+-privatepackage: com.example.internal.*
+This instruction is useful for fine-grained control over bundle contents during the build process.
+TODO Needs review - AI Generated content
+Example: -profile: "[<[profile]>]<[key]>
Pattern: .*
The -profile instruction sets a prefix (profile key) that is used when a variable is not found in the current context. If a variable is missing, bnd will re-search for it using the pattern [<profile>]<key>, where <profile> is the value of the -profile instruction and <key> is the variable name.
This allows you to define profile-specific values for variables, making it easier to manage different build or runtime configurations within the same project. If no profile is set or the variable is not found under the profile, bnd will fall back to the default value.
+TODO Needs review - AI Generated content
+Example: -provider-policy: ${range;[==,=+)}
Pattern: .*
The -provider-policy instruction defines the semantic versioning policy to be used when a type is a provider. A provider is in general a type that is implemented by classes that are responsible for the contract implied by the package. In contrast, a consumer is the party that just uses the contract defined in the package. For example, when you implement Event Admin, the org.osgi.service.event package is your responsibility so the types you need to implement like EventAdmin are provider types. (These types are annotated with a @ProviderType annotation.) A casual user of the Event Admin service will be a consumer, the EventHandler type is therefore annotated with a@ConsumerType.
The purpose of this distinction is semantic versioning. It turns out that the relation between a consumer and a provider is not symmetric. A provider is tightly bound to a contract while a consumer is expected to have backward compatibility. Virtually any change to the contract requires the provider to adapt while a consumer is in almost all cases protect against changes.
+This asymmetry has a consequence for the semantic versioning. In the OSGi, the semantics are defined that a micro change does not affect the provider nor the consumer. A minor change affects the provider, and a major change affects both. Therefore, a bundle that implements a provider type must import a range from major.minor.micro ... major.minor+1.0. A bundle that implements a consumer type must import major.minor.micro ... major+1.0.0.
In theory, bnd could have hard coded these policies but there are always cases where the policy is just not right. The -provider-policy specifies the macro to use for calculating the version range. The default definition is:
-consumer-policy ${range;[==,+)}
+-provider-policy ${range;[==,=+)}
+The range macro works very much like the version macro. It uses a template to define a change the range/version.
+The provider and consumer policy are global and this is not very convenient if you want to make an exception just for a specific bundle. For example, a bundle coming from Gavin King's Ceylon. For this reason, you can also specify a policy on an import:
+Import-Package com.gavinking.*;version="${range;[--,++)}", *
+The counterpart of the -provider-policy is of course the -consumer-policy.
Example: -releaserepo=cnf
Values: ${repos}
Pattern: .*
You can specify zero or more repository names to use when releasing a project.
+The -releaserepo instruction aggregates the -releaserepo property and all properties that start with -releaserepo.. This makes it possible to set release repository names in different places.
If the -releaserepo instruction is set to the empty value, then releasing a project does nothing. If the -releaserepo instruction is unset, then releasing a project will release it to the first writable repository.
Example:
+-releaserepo: releases, snapshots
+This instruction is useful for managing release targets in multi-repository environments.
+TODO Needs review - AI Generated content
+Example: -remoteworkspace: true
Values: true,false
Pattern: true|false|TRUE|FALSE
Launchpad is a library that enables testing in local JUnit settings. Launchpad needs access to the enclosing +bnd workspace. However, this workspace runs in another process then the test code. Launchpad will therefore +attempt to a workspace remote server.
+For security reasons, this remote workspace server is not enabled by default. It requires:
+-remoteworkspace true
+Remote Workspace servers can be nested. That is, you can run Eclipse and then Gradle on the same workspace. +Launchpad will use the latest initialized remote workspace.
+If you enable the remote workspace, its socket's port will be registered in the /cnf/cache/remotews directory.
The remote workspace server can only be accessed from the local machine on 127.0.0.1 to prevent outside +attacks.
+Example: -removeheaders=FOO_.*,Proprietary
Pattern: .*
The -removeheaders instruction allows you to remove specific headers from the manifest of your bundle. You provide a list of header names or patterns, and any matching headers will be excluded from the final manifest. This is useful for cleaning up or customizing the manifest to meet specific requirements.
TODO Needs review - AI Generated content
+Example: -exportreport: metadata.json;configName=blueprint
Pattern: .*
The purpose of the -reportconfig instruction is to configure the content of the reports exported with the -exportreport instruction.
When a report is generated, a set of plugins is used to extract a specific piece of data from the source (for example, the information contain in metatypes from a bundle source). Those plugins are generally designed to not require a configuration and are silently ignored if they do not find any data, thus, this instruction should rarely be used.
+Additional plugins can be declared and configured with the -plugin.* instruction and will be available for all your reports, however the -reportconfig instruction gives more control on the plugins that should be used when generating a specific report. This instruction diverges from the -plugins.* instruction as you can declare named configuration, for example -reportconfig.api-bundle:... will have the name api-bundle that you can then use for a specific report -exportreport:file.json;configName=api-bundle. In addition, this instruction allows to declare plugins with a short name instead of the canonical name of the plugin class (importFile instead of biz.aQute.bnd.reporter.plugins.entries.any.ImportResourcePlugin).
See -exportreport instruction documentation.
+-reportconfig.xxx ::= plugin-def ( ',' plugin-def ) *
+plugin-def ::= plugin | 'clearDefaults'
+plugin ::= ( qname | plugin-name ) ( ';' parameters ) *
+plugin-name ::= extended
+where xxx is the name of the configuration.
+One use case is when you want a specific resource from a bundle in the report but where there is no plugins to extract it. For this you can use the importJarFile plugin which need the path to the resource inside the bundle and will import it into the report.
For example, if you need blueprint data:
+bnb.bnd
+-reportconfig.blueprint: importJarFile;path=OSGI-INF/blueprint/component.xml
+-exportreport: metadata.json;configName=blueprint
+When you set a -reportconfig.xxx instruction, a list of default plugins will be added to the specified list. If you do not want the default plugins you can use the special property clearDefaults:
bnb.bnd
+-reportconfig.blueprint: \
+ importJarFile;path=OSGI-INF/blueprint/component.xml, \
+ clearDefaults
+This section describes the available plugins in Bnd, additional plugins may be provided by a specific build tool.
+All plugins have the entryName property which can be set to override the name under which data will be aggregated into the reports (this corresponds to a tag name when serialised in XML):
-reportconfig.bundle: metatypes;entryName=serviceConfigs
+This plugin allows the user to define an arbitrary entry.
+anyEntrykey: the name under which the value will be available in the report.value: the value of the entry.-reportconfig.api-bundle:anyEntry;key=bundleType;value=api
+This plugin allows to add a local or remote file to the report. The type of the file can be: properties, manifest, XML and JSON.
importFileurl: URL to the file.type: The type of the file. (optional)-reportconfig.bundle:importFile;url=http://<...>/myFile.json
+This plugin allows to add a file contains in a bundle to the report. The type of the file can be: properties, manifest, XML and JSON.
importJarFilepath: Path from the root of the Jar.type: The type of the file. (optional)-reportconfig.bundle:
+Add some common data to the report. If data are extracted from the workspace, the following properties will be read in the build.bnd file: ws-name, ws-description, ws-version, ws-icons, ws-docURL, ws-updateLocation, ws-licenses, ws-developers, ws-scm, ws-copyright, ws-vendor, ws-contactAddress; otherwise, the corresponding headers will be read. If data are extracted from a project, the above properties will be read in the bnd.bnd file but with a p- prefix, such as p-contactAddress.
commonInfoAdd the OSGI headers to the report.
+manifestAdd a list of the declarative services.
+componentsAdd a list of the metatypes.
+metatypesAdd the file name or the folder name in which the source is backed up.
+fileNameAdd a list of bundle data (for example, the bundles built by a project).
+bundlesuseConfig: The configuration name used when generating the data. (optional)excludes: A list of bundle symbolic names to exclude. (optional)-reportconfig.project:bundles;useConfig=api-bundle;excludes="com.domain.product.provider"
+Add a list of code snippet extracted from source files in a project. By default, source files containing code snippets must be located in a <root test source dir>/examples folder and its sub directories (eg; src/test/java/examples/**). The actual supported file types are: java.
Code snippets can either be a single sample code with a title and a description or have multiple steps.
+codeSnippetspath: The directory path from which code snippets will be looked up. (optional)You can either export a full type or a method. For the snippet to be extracted you must add a comment block to the type or the method. The comment must contain the ${snippet } tag with the following optional properties:
title (String): A title for the code snippet.description (String): A short description of the code snippet.includeDeclaration (Boolean): If true, the type declaration (or the method declaration) will be include, otherwise only its content. Default is true.includeImports (Boolean): If true, the imports declration will be include. Default is true.groupName (String): A user defined group name. If set, it will turn the actual snippet into a snippet with multiple steps, only the title and the description considered then and the snippet children must reference its groupName in their parentGroup property.parentGroup (String): The parent group name. If set, it will make the actual snippet a step in the referenced snippet group.id (String): A user defined id of the snippet. By default, the Id is the type name or the method name, optionally appended by a counting starting from 1 if not unique (eg; MyType, MyType1, etc).Here is an example illustrating a code snippet with steps:
+/**
+ * ${snippet title=How to Print Something in Java, description="In this example we will show how to
+ * print a string to the console.", groupName=print}
+ */
+public class PrintExample {
+
+ /**
+ * ${snippet title=Print No New Line, description="Here, we print without a new line at the end.",
+ * parentGroup=print, includeDeclaration=false}
+ */
+ public void printNoNewLine() {
+ System.out.print("Hello");
+ }
+
+ /**
+ * ${snippet title=Print With New Line, description="Here, we print with a new line at the end.",
+ * parentGroup=print, includeDeclaration=false}
+ */
+ public void printWithNewLine() {
+ System.out.println("Hello");
+ }
+}
+Which will result in the following json object:
{
+ "id": "PrintExample",
+ "title":"How to Print Something in Java",
+ "description":"In this example we will show how to print a string to the console.",
+ "steps":[
+ {
+ "id": "printNoNewLine",
+ "title":"Print No New Line",
+ "description":"Here, we print without a new line at the end.",
+ "programmingLangague":"java",
+ "codeSnippet":"System.out.print(\"Hello\");"
+ },
+ {
+ "id": "printWithNewLine",
+ "title":"Print With New Line",
+ "description":"Here, we print with a new line at the end.",
+ "programmingLangague":"java",
+ "codeSnippet":"System.out.println(\"Hello\");"
+ }
+ ]
+}
+Add a list of project data (for example, the projects built by the workspace).
+projectsuseConfig: The configuration name used when generating the data. (optional)excludes: A list of project names to exclude.-reportconfig.ws:projects;useConfig=bnd-proj;excludes="maven-index"
+Add the maven coordinate of the bundle (extracted from the pom.properties file).
+mavenCoordinateAdd a list of Gogo commands.
+gogoCommandsExample: -reportnewer=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -reportnewer instruction reports any entries that were added to the build since the last JAR was made. This is useful for tracking changes and ensuring that only new or updated files are included in the build output.
Example:
+-reportnewer: true
+When enabled, bnd will output a list of files that are newer than the previous build.
+TODO Needs review - AI Generated content
+Example: -reproducible=true
Values: true,false
Pattern: true|false|TRUE|FALSE
To ensure the bundle can be built in a reproducible manner, the timestamp of the zip entries is set to the fixed time 1980-02-01T00:00:00Z when the value of this instruction is true.
+The value can also be set to either an ISO-8601 formatted time or the number of seconds since the epoch which is used as the timestamp for the zip entries.
+The Bnd-LastModified header is also omitted from the manifest.
+The default value is false.
For example:
+-reproducible: true
+-reproducible: 1641127394
+-reproducible: 2022-01-02T12:43:14Z
+Example: -require-bnd="(version>=4.1)"
Values: (FILTER ( ',' FILTER )* )?
Pattern: .*
The -require-bnd instruction allows you to specify a filter that must match the Bnd version for the build to proceed. If the filter does not match, bnd will generate an error and stop the build. This is useful for enforcing minimum or specific Bnd versions in your build environment.
Example:
+-require-bnd: version>=7.0.0
+If not set, any Bnd version will be accepted.
+Each specified filter must evaluate to true for the running version of Bnd in the version attribute. Since the values of the instruction are filter expressions, they need to quoted so the filter operators are not processed by Bnd.
This instruction can be useful when the workspace requires a feature of Bnd introduced in some version of Bnd. For example:
+-require-bnd: "(version>=4.3.0)"
+TODO Needs review - AI Generated content
+Example: -resolve.effective=resolve,active
Values: qname (',' qname )
Pattern: .*
Each requirement and capability has an effective or is effective=resolve. An effective of resolve is always processed by the resolver.However, in (very) special cases it is necessary to provide more rules.
The -resolve.effective syntax is as follows:
-resolve.effective ::= effective ( ',' effective )*
+effective ::= NAME (';skip:=' skip )
+skip ::= skip = '"' namespace ( ',' namespace ) * '"'
+The simplest model is to just list the names, for example:
+-resolve.effective: resolve,active
+In this case, the resolver will only look at requirements that are either resolve or active.
+Adding a meta effective could then be:
-resolve.effective: resolve,active, meta
+However, in very, very rare (usually error) cases it is necessary to exclude certain namespaces. This can be done by using the skip: directive.
-resolve.effective: resolve,active, meta;skip:='osgi.extender,osgi.wiring.package'
+Values: true|false
Pattern: .*
This property has no meaning in the normal configuration. It is used by code that needs to +have the system resource in the wiring. Normally the wiring excludes the system resource. +However, sometimes it is necessary to see the full solution.
+Example: -resolve: '-resolve manual
Values: manual,auto,beforelaunch,batch,cache,never
Pattern: (manual|auto|beforelaunch|batch)
manual Resolve is manually with the Resolve buttonValues: manual
Pattern: \Qmanual\E
auto A resolve will take place before saving
Values: auto
Pattern: \Qauto\E
beforelaunch A resolve will take place before launching
Values: beforelaunch
Pattern: \Qbeforelaunch\E
batch A resolve will take place before launching when in batch mode (e.g. Gradle) but not in IDE mode (e.g. Eclipse)
Values: batch
Pattern: \Qbatch\E
cache Resolve when the runbundles are needed unless there is a cache file that is newer than the bndrun/project & workspace. The cache file has the same name as the project/bndrun file but starts with a '.'
Values: cache
Pattern: \Qcache\E
never A Resolve will never take place. Manually resolve will result in error.
Values: never
Pattern: \Qnever\E
The bnd workspace can use a resolver to calculate the content of the -runbundles instruction based on a set of initial requirements. The bndtools GUI can manually resolve the initial requirements but through the -resolve instruction it is possible to calculate the -runbundles when the file is saved or just before the -runbundles are used in the launch.
The values are:
+manual – It is up to the user to resolve the initial requirementsauto – Whenever the initial requirements are saved, the resolver will be used to set new -runbundlesbeforelaunch – Calculate the -runbundles on demand. This ignores the value of the -runbundles and runs the resolver. The results of the resolver are cached. This cache works by creating a checksum over all the properties of the project.batch – When running in batch mode, the run bundles will be resolved. In all other modes this will only resolve when the -runbundles are empty.cache – Will use a cache file in the workspace cache. If that file is stale relative to the workspace or project or it does not exist, then the bnd(run) file will be resolved and the result is stored in the cache file.never – If anybody tries to resolve , the process will throw an UnsupportedOperationException. This is intended for manually curated runbundles or where a base file is resolved and other files include them an add some additional instructions. The Excepetion is meant as a clear warning to any developer accidentally resolving the -runbundles.-resolve beforelaunch
+Example: -resolve.preferences=com.example.bundle.most.priority
Values: ${packages}
Pattern: .*
The resolver normally finds a lost of capabilities that match a given requirement. This list has an order defined by the context. However, in certain occasions this order is not the desired order. The -resolve.preferences allows you to override this context order. It is an ordered list of Bundle Symbolic Names. The list of capabilities will always be adjusted to have the bundles in the -resolver.preferences always first when they are present.
For example:
+`-resolve.preferences` : \
+ com.example.bundle.most.priority, \
+ com.example.bundle.less.priority, \
+ com.example.whatever
+Given that for a requirement the capabilties come from:
+com.example.some.bundle,
+om.example.bundle.less.priority,
+com.example.another.bundle,
+com.example.most.priority
+Then the resulting order will be:
+com.example.most.priority
+om.example.bundle.less.priority,
+com.example.some.bundle,
+com.example.another.bundle,
+Preferences should only be used when blacklisting is not a better solution.
+Example: `-resolve.reject foo;filter:='(foo=3)``
+Pattern: .*
The resolver will request capabilities from the resolve context. In bnd, the resolve hook can be
+used to reject some of these capabilities. The -resolve.reject instruction can control the default
+hook. Without this instruction, nothing is rejected and the resolver sees all capabilities. With this
+hook it is possible to reject
To directly reject a namespace foo, the instruction is simply:
-resolve.reject foo
+This will reject any capability in the namespace foo. To reject a namespace foo where a filter matches, the instruction is:
-resolve.reject foo;filter:='(foo=3)`
+This will reject any capability in the namespace foo that has an attribute foo that has the value 3.
Sometimes it is necessary to reject a capability depending on its resource. For example, you want to reject
+capabilities that come from fragments. You can achieve this by prefixing the requirement with
+a @ (commercial at sign). For example, if you want to reject any capabilities that come from a resource that has identity type osgi.fragment.
-resolve.reject @osgi.identity;filter:='(type=osgi.fragment)'
+You can add multiple requirements in the specification. These requirements will be or'ed together. The following + specification:
+-resolve.reject @foo, baz
+Will reject any capabiliy from a resource that has a foo capability and it will remove all baz capabilities.
Before bnd release 7, the resolver always rejected resources that were neither a bundle nor a fragment. This was +changed to allow more scenarios. The filtering was moved to the list of bundles where it is necessary that the resources +are all bundles.
+This should normally not make a difference during resolving. However, in some cases you want to have the old +situation. The following example restores the pre release 7 situation.
+-resolve.reject @osgi.identity;filter:='(!(|(type=osgi.bundle)(type=osgi.fragment)))'
+TODO Needs review - AI Generated content
+Example: -resolvedebug: 1
Values: 0,1,2,3
Pattern: .*
When resolving, the -resolvedebug instruction can be used to request debug information about the resolve is displayed to System.out. The value 0 turns off displaying debug information. The values 1, 2, and 3 display progressively more debug information.
-resolvedebug: 1
+Example: -resourceonly=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -resourceonly instruction tells bnd to ignore warnings if the bundle contains only resources and no classes. This is useful for bundles that are intended to provide configuration files, images, or other non-code assets.
Example:
+-resourceonly: true
+When set, bnd will not warn about the absence of classes in the bundle.
+TODO Needs review - AI Generated content
+Example: -runblacklist=osgi.identity;filter:='(osgi.identity=<bsn>)', ...
Pattern: .*
The blacklist is a set of requirements. These requirements are used to get a set of resources from the repositories that match any of these requirements. This set is then removed from any result from the repositories, effectively making it impossible to use.
+For example:
+-runblacklist: \
+ osgi.identity;filter:='(osgi.identity=com.foo.bad.bundle)'
+Example: -runbuilds=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -runbuilds instruction controls whether the bundles built by the current project should be automatically added to the -runbundles list when running or testing the project.
.bndrun file, the default is false, so built bundles are not added unless you explicitly set -runbuilds: true..bnd file, the default is true, so built bundles are included unless you set -runbuilds: false.This instruction is useful for controlling which bundles are available at runtime, especially when you want to test or launch only a subset of the bundles produced by your project.
+TODO Needs review - AI Generated content
+Example: -runbundles=osgi;version="[4.1,4.2)", junit.junit, com.acme.foo;version=project
Pattern: [-\w]+(:?\.[-\w]+)*
version Specifies the range in the repository, project or file.Example: version=project
Values: project,type
Pattern: project|type|((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
The runbundles instruction is used to specify which bundles should be installed when a framework is started. This is the primary mechanism to run applications directly from bnd/bndtools. A bundle listed in -runbundles can be either a workspace bundle (a bundle created by one of the workspace's projects) or a bundle from one of the configured repositories. Note that all required bundles to run the application should be listed, transitive dependencies are not handles automatically so that there is full control over the runtime.
+This list can be maintained manually it is normally calculated by the resolver. That is, when a resolve is run then it will, without warning, override this list.
+For example:
+-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\
+ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+ ...
+ osgi.enroute.twitter.bootstrap.webresource;version='[3.3.5,3.3.6)',\
+ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1)'
+Some launchers support startlevels and the -runbundles instruction therefore has a startlevel attribute. This attribute
+must be a positive integer larger than 0.
-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)'; startlevel=100,\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)'; startlevel=110,\
+ ...
+Since the common workflow is to use the resolver to calculate the set of run bundles, any start level settings +would be overridden after the next resolve. There are the following solutions.
+Use the -runstartlevel instruction to let the resolver calculate the start level ordering. In that case the
+resolver will add the startlevel attribute.
Use the decoration facility. With the decoration facility you can augment the -runbundles instruction by
+specifying the -runbundles+ property (or the -runbundles++ if you want to add literals). The keys are glob expressions
+and any attributes or directives will be set (or overridden) on the merged -runbundles instruction.
-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\
+ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+
+-runbundles+: \
+ org.apache.felix.servlet-api;startlevel=100, \
+ *;startlevel=110
+Example: -runee=JavaSE-1.8
Pattern: .*
The -runee instruction adds the capabilities of an execution environment to the system capabilities. Every Java edition has a set of standard packages and OSGi has also defined a number of execution environments that define which packages can be found. The -runee allows these capabilities to be defined by using the name of the execution environment. Additionally, this instruction also adds an osgi.ee requirement with the given name and version. You can use the following execution environment names:
OSGi/Minimum-1.0
+OSGi/Minimum-1.1
+OSGi/Minimum-1.2
+JRE-1.1
+J2SE-1.2
+J2SE-1.3
+J2SE-1.4
+J2SE-1.5
+JavaSE-1.6
+JavaSE-1.7
+JavaSE/compact1-1.8
+JavaSE/compact2-1.8
+JavaSE/compact3-1.8
+JavaSE-1.8
+JavaSE-9
+An example:
+-runee: JavaSE-1.8
+Example: -runenv: org.osgi.service.http.port=9999, org.osgi.framework.bootdelegation="sun.*,com.sun.*,"
Pattern: .*
The -runenv instruction specifies system properties to set when launching the application. This is useful for configuring the runtime environment, such as setting ports, feature flags, or other JVM properties.
Example:
+-runenv: my.property=value, another.property=1234
+These properties will be set in the local JVM when the workspace is started.
+TODO Needs review - AI Generated content
+Example: -runfw: none
Values: (none | services | ANY)
Pattern: .*
The -runframework instruction sets the type of framework to run when launching an OSGi application. If set to none, an internal dummy framework is used. If set to services or any other value, the Java META-INF/services mechanism is used to locate the FrameworkFactory implementation.
Note: The name of this instruction is somewhat confusing due to historical reasons. In most cases, you should use the -runfw instruction to specify the actual framework JAR. The -runframework instruction is only needed if you want to control the framework discovery mechanism or use a dummy framework for testing purposes.
TODO Needs review - AI Generated content
+Example: -runframeworkrestart: true
Pattern: .*
Any bundle can stop the framework. After stopping, the launcher receives a notification and normally
+exit the process with System.exit. In some cases, usually testing, it is necessary to do a restart
+in the local VM.
If -runframeworkrestart is set to true, the launcher will not do a hard exit after the framework is stopped,
+but will restart the framework after doing the normal clean up.
The launcher keeps a system property launcher.framework.restart.count that is set to the iteration, it is initially zero.
Example: -runfw: org.eclipse.osgi; version=3.10
Pattern: .*
The -runfw instruction sets the framework to use. This framework will be added to the -runpath. Any exported packages or capabilities listed in the manifest of the framework are automatically added to the system capabilities.
For example:
+-runfw: org.eclipse.osgi; version=3.10
+Note – Do not use runframework, this instruction is deprecated and had very different intent and syntax.
Example: -runjdb: localhost:10001
Pattern: .*
The -runjdb instruction specifies a JDB (Java Debugger) socket transport address to use when launching the application outside a debugger. This allows the debugger to attach to the running process for debugging purposes.
Example:
+-runjdb: localhost:10001
+The address can include a host name or IP address and a port. This is useful for remote debugging scenarios.
+This instruction launches the VM with the
+-agentlib:jdwp=transport=dt_socket,server=y,address=<address>,suspend=y
+command line argument.
+The socket transport address can include a host name (or IP address) and a port. For example:
+-runjdb: localhost:10001
+The socket transport address can be just a port number. For example:
+-runjdb: 10001
+Note: Starting with Java 9, using just a port number means that the launched VM will only listen on localhost for the connection. If you want to remote debug, you will need to specify a host name (or IP address), or an asterisk (*) to accept from any host. For example:
-runjdb: *:10001
+If the socket transport address starts with a minus sign (-), then the launched VM is not suspended: suspend=n. The minus sign is removed from the socket transport address before it is used for the address option.
If the specified socket transport address is not a port number or a host:port value, then the address 1044 is used.
TODO Needs review - AI Generated content
+Example: -runkeep: true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -runkeep instruction decides whether to keep the framework storage directory between launches. When set to true, the storage directory is preserved, which can be useful for debugging or maintaining state between runs.
The -runkeep instruction is particularly useful in scenarios where you need to persist data across different runs of the framework. For instance, if your application generates logs, caches data, or requires a specific directory structure to function correctly, setting -runkeep to true will ensure that these elements are not removed between launches.
However, one should be cautious while using this option, as preserving the storage directory might lead to outdated or corrupted data being used in subsequent runs. It is generally recommended to use the default setting (i.e., -runkeep: false) unless there is a specific need to retain the storage directory.
Example:
+-runkeep: true
+By default, the storage directory is deleted between launches.
+TODO Needs review - AI Generated content
+Example: -runnoreferences: true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -runnoreferences instruction controls whether the reference: URL scheme is used for installing bundles in the OSGi installer. On non-Windows systems, reference: URLs are used by default for efficiency. Setting -runnoreferences: true disables this behavior and uses regular file URLs instead. On Windows, this instruction is ignored because reference: URLs are never used due to file locking issues.
Example:
+-runnoreferences: true
+Use this instruction if you need to avoid reference: URLs for compatibility or deployment reasons.
TODO Needs review - AI Generated content
+Example: -runoptions: -runoptions eager
Pattern: .*
Example: -runpath=org.eclipse.osgi;version=3.5
Pattern: .*
version Specifies the range in the repository, project or file.Example: version=project
Values: project,type
Pattern: project|type|((\(|\[)\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?,\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?(\]|\)))|\d{1,9}(\.\d{1,9}(\.\d{1,9}(\.[-\w]+)?)?)?
An OSGi application will have a set of bundles and an environment created by the framework and any additional JARs on the class path. The -runpath instruction sets these additional bundles. These JARs can actually export packages and provide capabilities that the launcher will automatically add to the system capabilities. The resolver will do the same. Any packages exported by bundles or provided capabilities on the -runpath are automatically added to the system capabilities.
The -runpath instruction allows you to specify additional JARs that should be included on the class path of the remote VM when running an OSGi application. These JARs can export packages and provide capabilities that are automatically added to the system capabilities, making them available to the resolver and the launcher.
This is particularly useful for including frameworks or other dependencies that are not part of the main set of bundles but are required for the application to function correctly.
+For example:
+-runpath: \
+ com.foo.bar;version=1, \
+ file.jar; version=file
+TODO Needs review - AI Generated content
+Example: -runprogramargs=/some/file, /another/file, some_argument
Pattern: .*
The -runprogramargs instruction allows you to specify additional arguments that will be passed to the main program when it is launched. These arguments are appended to the command line after all other options and can be used to control the behavior of your application at runtime.
This is useful for providing custom parameters, flags, or configuration values to your application without modifying the code or build process.
+TODO Needs review - AI Generated content
+Example: -runproperties= foo=3, bar=4
Pattern: .*
The -runproperties instruction allows you to define system properties that will be set for the remote Java Virtual Machine (JVM) when your application is launched. These properties are passed as -Dkey=value arguments and can be used to configure the runtime environment, control application behavior, or provide configuration values.
This is useful for setting environment-specific options or overriding default values without modifying your application's code.
+TODO Needs review - AI Generated content
+Example: -runprovidedcapabilities=some.namespace; some.namespace=foo
Pattern: .*
The -runprovidedcapabilities instruction is used with the -distro instruction to specify extra capabilities that are provided by the target system but not listed in the distro file. This helps the resolver understand what is available at runtime.
The -runprovidedcapabilities instruction allows you to declare additional capabilities for a distribution that are not explicitly mentioned in the distribution file. By using this instruction, you enable the resolver to take into account these extra capabilities when determining the set of available features and functionalities in the target environment. This can be particularly useful in scenarios where certain capabilities are provided by the system but are not included in the default distribution configuration.
Example:
+-runprovidedcapabilities: some.namespace;some.namespace=foo
+This instruction is only relevant when working with distribution-based resolves.
+TODO Needs review - AI Generated content
+Example: -runremote= local; shell=4003; jdb=1044; host=localhost
Pattern: .*
Example: -runrepos=Maven Central, Main, Distro, ...
Pattern: .*
The -runrepos instruction is used to restrict or order the available repositories. A bndrun file (see Resolving) can be based on a workspace or can be standalone. In the workspace case the, the repositories are defined in build.bnd or in a *.bnd file in the cnf/ext directory as bnd plugins. In the standalone case the repositories are either OSGi XML repositories listed in the -standalone instruction or they are also defined as plugins but now in the bndrun file.
In both cases there is an ordered list of repositories. In the -standalone it is easy to change this order or exclude repositories. However, in the workspace case this is harder because the set of repositories is shared with many other projects. The -runrepos can then be used to exclude and reorder the list repositories. It simply lists the names of the repositories in the desired order. Each repository has its own name.
If -runrepos is ommited then all repositories having either no tags or the tag resolve will be included for resolution.
+You can exclude certain repositories by assigning it a tag different than resolve (e.g. <<EMTPY>> or foobar). See Tagging of repository plugins for more details.
Note The name of a repository is not well defined. It is either the name of the repository or the toString() result. In the later case the name is sometimes a bit messy.
Example 1: include specific repos
+-runrepos: Maven Central, Main, Distro
+This includes exactly the three repositories.
+Example 2: include all repos
+-runrepos instructionresolvee.g. a .bndrun file without -runrepos would include the following repos in the resolution:
aQute.bnd.repository.maven.provider.MavenBndRepository;\
+ tags = "resolve"; \
+ name="Maven Central A";\
+ releaseUrl="${mavencentral}";\
+ snapshotUrl="${ossrh}";\
+ index="${.}/central.mvn";\
+ readOnly=true,\
+because it has the resolve tag
and also
+aQute.bnd.repository.maven.provider.MavenBndRepository;\
+ name="Maven Central B";\
+ releaseUrl="${mavencentral}";\
+ snapshotUrl="${ossrh}";\
+ index="${.}/central.mvn";\
+ readOnly=true,\
+because there is no tags at all.
+Example 3: include all repos, except some
+-runrepos instructionresolve which should be included<<EMTPY>> or something else than resolve e.g. foobarFor example the following repository definition in /cnf/build.bnd would be excluded:
aQute.bnd.repository.maven.provider.MavenBndRepository;\
+ tags = "<<EMPTY>>"; \
+ name="Maven Central";\
+ releaseUrl="${mavencentral}";\
+ snapshotUrl="${ossrh}";\
+ index="${.}/central.mvn";\
+ readOnly=true,\
+because it has a <<EMPTY>> tag (and thus no resolve tag).
An example use case is exclude the baseline-repository from resolution. In the case, you would not add the resolve tag to the baseline-repo, and then it won't be considered for resolution in a .bndrun.
Example: -runrequires=osgi.identity;filter:='(osgi.identity=<bsn>)', ...
Pattern: .*
The -runrequires instruction specifies root requirements for a resolve operation. These requirements are input into the resolver
+which generates a list of bundles (the -runbundles list) that can satisfy the requirements.
Each requirement must be in the format of an entry in the OSGi standard Require-Capability header. See Section 3.3.6 ("Bundle Requirements") of OSGi Core Release 6 specification.
A requirement can specify any arbitrary namespace, including but not limited to those listed in Chapter 8 ("Framework Namespaces Specification") of OSGi Core Release 6 specification and Chapter 135 ("Common Namespaces Specification") of OSGi Compendium Release 6.
+Bnd also supports alias namespaces -- see below.
+-runrequires: \
+ osgi.identity; filter:='(osgi.identity=org.example.foo)',\
+ osgi.identity; filter:='(&(osgi.identity=org.example.bar)(version>=1.0)(!(version>=2.0)))'
+This specifies a requirement for the resource identified as "org.example.foo" (with any version) AND the resource identified as "org.example.bar" with version in the range 1.0 inclusive to 2.0 exclusive.
+To ease manual entry of requirements bnd supports alias namespaces which translate to standard namespaces and filters.
+In all cases, attributes and directives that are not consumed by the alias are passed through to the generated requirement. For example if the bnd.identity alias is used with a directive of resolution:=optional then the generated osgi.identity requirement shall also have the directive resolution:=optional.
The following aliases are supported:
+bnd.identityThe bnd.identity namepace alias takes the following attributes:
id: the identity of the resourceversion: a version range in conventional OSGi form, e.g. [1.0, 2.0).Example:
+-runrequires:\
+ bnd.identity; id=org.example.foo,\
+ bnd.identity; id=org.example.bar; version=1.0,\
+ bnd.identity; id=org.example.baz; version='[1.0,2.0)'
+is translated to:
+-runrequires:\
+ osgi.identity; filter:='(osgi.identity=org.example.foo)',\
+ osgi.identity; filter:='(&(osgi.identity=org.example.bar)(version>=1.0))',\
+ osgi.identity; filter:='(&(osgi.identity=org.example.baz)(version>=1.0)(!(version>=2.0)))'
+bnd.literalThe bnd.literal alias can be used if you need to create a literal requirement that has a namespace clashing with one of the aliases. For example if you want to have a requirement with the literal namespace bnd.identity, i.e. not processed as an alias.
Only one attribute is used:
+bnd.literal: specifies the literal namespaceExample:
+-runrequires:\
+ bnd.literal; bnd.literal=bnd.identity; filter:='(bnd.identity=foo)',\
+ bnd.literal; bnd.literal=bnd.literal; filter:='(bnd.literal=bar)'
+is translated to:
+-runrequires:\
+ bnd.identity; filter:='(bnd.identity=foo)',\
+ bnd.literal; filter:='(bnd.literal=bar)'
+Example: -runstartlevel: '-runstartlevel order=leastdependenciesfirst, begin=1, step=1
Pattern: .*
begin Beginning automatic startlevel calculation, -1 indicates no automatic calculation. When bdn calculates the startlevel, this will be the first assigned startlevelExample: begin='begin=10
Pattern: \d+
step Start level step for each next bundle. Startlevel is 0 when < 1
Example: step='begin=1
Pattern: \d+
order Ordering of -runbundles of the resolved bundles
Example: order='order=leastdependencieslast
Values: LEASTDEPENDENCIESFIRST,LEASTDEPENDENCIESLAST,RANDOM,SORTBYNAMEVERSION,MERGESORTBYNAMEVERSION
Pattern: .*
Options:
+LEASTDEPENDENCIESFIRST Order the -runbundles by having the least dependent first.Values: LEASTDEPENDENCIESFIRST
Pattern: \QLEASTDEPENDENCIESFIRST\E
LEASTDEPENDENCIESLAST Order the -runbundles by having the least dependent last.
Values: LEASTDEPENDENCIESLAST
Pattern: \QLEASTDEPENDENCIESLAST\E
RANDOM Order the -runbundles randomly using the Collections#shuffle.
Values: RANDOM
Pattern: \QRANDOM\E
SORTBYNAMEVERSION Order the -runbundles sorted by name.
Values: SORTBYNAMEVERSION
Pattern: \QSORTBYNAMEVERSION\E
MERGESORTBYNAMEVERSION Order the -runbundles sorted by name and merged with the existing value if it exists. This is the default since it was the classic behavior.
Values: MERGESORTBYNAMEVERSION
Pattern: \QMERGESORTBYNAMEVERSION\E
After a resolve the resolver calculates a number of resources that are mapped to bundles. This mapping can
+include ordering and assigned startlevels. The basic instruction that parameterizes this is -runstartlevel.
If -runstartlevel is not set the set of -runbundles will be sorted by name and version after which it is merged with the existing
+-runbundles. Setting the -runstartlevel makes it possible to let bnd assign startlevels based on different
+ordering strategies.
This instruction has the following syntax:
+-runstartlevel ::= runstartlevel ( ',' runstartlevel )
+runstartlevel :: order | begin | step
+order ::= 'order=' ORDER
+ORDER ::=
+ 'leastdependenciesfirst'
+ | 'leastdependencieslast'
+ | 'random'
+ | 'sortbynameversion'
+ | 'mergesortbynameversion'
+begin ::= 'begin=' NUMBER
+step ::= 'step=' NUMBER
+The final -runstartlevel flattens the properties so that the last of each of order, begin, or step will be used.
The value of order can take on the following values:
mergesortbynameversion – Ordering by name (and version) and then merging was the original behavior. This is therefore the default.sortbynameversion – For completeness, this option orders the bundles by name and version and then assigns a startlevel.random – Use a random ordering. The ordering uses an algorithm that is based on the random number generator and should therefore
+ be different on each run.leastdependenciesfirst – Sort the resources topologically and place the resources with the least dependencies first.leastdependencieslast – Sort the resources topologically and place the resources with the least dependencies last.The topological sorting algorithm is based on Tarjan. It can handle cyclic dependencies well. However, cyclic dependencies +make the ordering not perfect.
+After the resources have been resolved they are sorted according to the order. If the begin attribute is set and
+higher than 0, the resources will be assigned a startlevel that starts at the given value. By default, the step for each
+bundle is 10. (A lesson taught to us by BASIC) The step can be overridden by setting the step value.
If the begin value is not set, or it is set to a value < 1, then no startlevel attribute is added. However, the
+order of the -runbundles will be in the specified order. In most cases, this is then some kind of natural ordering
+since launchers start these
If you set the step to 0 then all bundles will be assigned the same startlevel. However, the -runbundles has the proper
+order.
-runstartlevel: \
+ order = leastdependenciesfirst, \
+ begin = 1000, \
+ step = 1
+After resolving, this can generate:
+-runbundles: \
+ org.apache.felix.configadmin;version='[1.8.8,1.8.9)';startlevel=1000,\
+ org.apache.felix.http.jetty;version='[3.2.0,3.2.1);startlevel=1001',\
+ org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3);startlevel=1002',\
+ ...
+ osgi.enroute.twitter.bootstrap.webresource;version='[3.3.5,3.3.6);startlevel=1019',\
+ osgi.enroute.web.simple.provider;version='[2.1.0,2.1.1);startlevel=1020'
+Example: -runstorage= foo
Pattern: .*
The -runstorage instruction defines the directory to use for the framework's work area. This is where the OSGi framework stores its runtime data, such as bundle caches and configuration.
Example:
+-runstorage: fwstorage
+By default, this directory is set to fw under the project's target directory, but you can override it with this instruction.
TODO Needs review - AI Generated content
+Example: -runsystemcapabilities=some.namespace; some.namespace=foo
Pattern: .*
Although resolver analyses the -runpath (and thus -runfw) for system capabilities, this is not always sufficient. For example, if the target system has special hardware then this might be described with a capability. Such an external capability must be explicitly given to the resolver. These extra capabilities maybe given with the -runsystemcapabilities instruction.
Examples:
+explizitly use system capabilities (also to for viewing them ;-)
+-runsystemcapabilities=${native_capability}
+define specific e.g. windows host capabilities
+-runsystemcapabilities=\
+ osgi.native; \
+ osgi.native.osname:List<String>="Windows10,Windows 10,Win32"; \
+ osgi.native.osversion:Version="10.0.0"; \
+ osgi.native.processor:List<String>="x86-64,amd64,em64t,x86_64"; \
+ osgi.native.language=en_US
+other capabilities
+-runsystemcapabilities=some.namespace; some.namespace=foo
Pattern: .*
Example: -runsystempackages=com.acme.foo,javax.management
Pattern: .*
The resolver will analyse the -runpath and -runfw JARs for any exported packages and make these available as system packages to the bundles. However, in certain cases this automatic analysis does not suffice. In that case extra packages can be added. These packages must, of course, be available on the class path.
For example:
+-runsystempackages: \
+ com.sun.misc
+Example: -runsystemcapabilities=some.namespace; some.namespace=foo
Pattern: .*
The -runtimeout instruction allows you to specify a timeout duration for running or testing your project. The value should be a duration (such as 30 SECONDS, 5 MINUTES, etc.), and it controls how long bnd will wait for the process to complete before terminating it.
This is useful for preventing tests or application runs from hanging indefinitely, ensuring that your build or test process completes in a reasonable amount of time.
+TODO Needs review - AI Generated content
+Example: -runtrace: true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -runtrace instruction enables detailed tracing of the launched process. When set to true, bnd will output additional information about the launch sequence, configuration, and runtime events. This can be helpful for debugging, troubleshooting, or understanding the behavior of your application during launch.
Use this instruction if you need more insight into the launch process or want to diagnose issues with your runtime environment.
+TODO Needs review - AI Generated content
+Example: -runvm=-Xmax=30, -DsecondOption=secondValue
Pattern: .*
The -runvm instruction allows you to specify additional arguments that will be passed directly to the Java Virtual Machine (JVM) when launching your application. These arguments are added as-is to the command line and can be used to set system properties, enable debugging, adjust memory settings, or configure other JVM options.
This is useful for customizing the runtime environment of your application without modifying the code or build process.
+TODO Needs review - AI Generated content
+Example: -savemanifest=file.txt
Pattern: .*
The -savemanifest instruction allows you to write out the calculated manifest to a separate file after it has been generated. This is useful for debugging, auditing, or sharing the manifest independently of the JAR.
Example:
+-savemanifest: my-manifest.mf
+The specified file will contain the manifest as it would appear in the final bundle.
+TODO Needs review - AI Generated content
+Example: -sign=alias
Values: <alias> [ ';' 'password:=' <password> ] [ ';'* 'keystore:=' <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
Pattern: .*
The -sign instruction is used to sign the generated JAR file with a specified certificate or keystore. This is important for distributing secure and trusted bundles, especially in environments that require signed artifacts.
Example:
+-sign: mykeystore;alias=myalias
+This instruction ensures that the output JAR is cryptographically signed as part of the build process.
+A more detailed example using additional properties:
+-sign: mykeystore;alias=myalias;storepass=secret;keypass=secret;keystore=path/to/keystore.jks;digestalg=SHA-256
+In this example:
+- mykeystore is the name of the keystore.
+- alias specifies the certificate alias to use for signing.
+- storepass and keypass provide the passwords for the keystore and key.
+- keystore gives the path to the keystore file.
+- digestalg sets the digest algorithm (e.g., SHA-256) for the signature.
These options allow you to fully control the signing process and meet the requirements of your deployment environment.
+TODO Needs review - AI Generated content
+Example: -snapshot=${tstamp}
Pattern: .*
The -snapshot instruction specifies a string to substitute for "SNAPSHOT" in the bundle version's qualifier. This is useful for customizing versioning schemes during the build process.
Example:
+-snapshot: 20250708
+If the bundle version's qualifier is "SNAPSHOT" or ends with "-SNAPSHOT", the specified string will be used instead.
+When the bundle version's qualifier equals "SNAPSHOT" or ends with "-SNAPSHOT", the STRING
+value of the -snapshot instruction is substituted for "SNAPSHOT". The STRING value of
+the empty string will remove the qualifier if it equals "SNAPSHOT" or
+will remove "-SNAPSHOT" from the end of the qualifier.
The default is no substitution.
+For example, the bundle's version can be set with a "SNAPSHOT" qualifier indicating it is +a maven snapshot version.
+Bundle-Version: 3.2.0.SNAPSHOT
+When time to release, the -snapshot instruction can be used, in a central location such
+as cnf/build.bnd, to substitute "SNAPSHOT" for another value such as the timestamp of
+the build:
-snapshot: ${tstamp}
+or some other meaningful value:
+-snapshot: REL
+Using "SNAPSHOT" in the bundle version's qualifier and the -snapshot instruction
+are generally for bundles intended for maven builds.
TODO Needs review - AI Generated content
+Example: -sourcepath:= src, test
Pattern: .*
The -sourcepath instruction specifies a list of directory names that bnd should use to find source files. These directories are searched for Java source files and related resources when building your project or including sources in the bundle.
This is useful for ensuring that all relevant source files are available for compilation, packaging, or source inclusion in the generated JAR.
+TODO Needs review - AI Generated content
+Example: -sources=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -sources instruction tells bnd to include the source code (if available on the -sourcepath) in the bundle under the OSGI-OPT/src directory. This is useful for distributing source code along with your binary bundle, making it easier for others to debug or understand your code.
When enabled, bnd will collect the relevant source files and package them in the appropriate location within the bundle.
+TODO Needs review - AI Generated content
+Example: -stalecheck: \
+ specs/**.md; \
+ newer='doc/**.doc' ; \
+ error='Markdown needs to be generated'
Pattern: .*
If you work in Bndtools then there is normally little to worry about, bndtools keeps everything up to dated all the time. +Any file you save, and guaranteed in s short time all dependent bundles are updated. This comes with a crucial guarantee +that anything you do in Bndtools is faithfully reproduced on the build server using Gradle. For a default workspace there +is no need for any gradle specific files. All the workspace's settings are defined in the bnd files.
+However, sometimes it is necessary to generate sources that need to be compiled. There are then two choices when you +checkout a workspace. Always use the command line let the build do its magic or checkin the generated sources. +Although generally frowned upon, it has the advantage that you do not need to store the tool itself under version control. +Any revision can always compile regardless of what happened to the tool. It is also nice to see it on Github, it actually +makes the experience of the majority of people that use the workspace and usually not touch the swagger file blithely +unaware of the underlying complexities.
+However, this has a very serious problem. Sometimes you touch the swagger file and forget to run the gradle to generate
+new sources. It would be very convenient if bnd could generate an error if certain files were out of date respective
+to a set of other files. Thhe -stalecheck instruction has this purpose.
-stalecheck ::= clause ( ',' clause )*
+clause ::= src ';' 'newer=' depends (';' option )*
+src ::= Ant Glob spec
+depends ::= Ant Glob spec
+option ::= 'error=' STRING
+ | 'warning=' STRING
+ | 'command=' STRING
+Just before bnd will start making the JAR(s) for a project it will check if any of the files in the src specification
+is newer than the files in the depends set. If so, it will provide warning or error. If either a specific error or
+warning STRING is given then this is reported accordingly.
If a command STRING is given it is executed as in the ${system} macro. If the command STRING starts with a
+minus sign (-) then a failure is not an error, it is reported as warning.
If no command is given, nor an error or warning then a default error is reported.
+-stalecheck: \
+ openapi.json; \
+ newer='gen-src/, checksum.md5'
+
+-stalecheck: \
+ specs/**.md; \
+ newer='doc/**.doc' ; \
+ error='Markdown needs to be generated'
+Example: -standalone=index.html;name=..., ...
Pattern: .*
The -standalone instruction in bnd allows you to transform a bndrun file into a standalone file that doesn't require a workspace. It is a merged property, meaning you can specify additional repo-specs using -standalone.extra. However, the presence of the exact -standalone flag is what determines if the bndrun file is standalone. Without the -standalone flag, even if -standalone.extra is specified, a workspace will still be required.
A bndrun file is by default connected to its workspace, where the workspace defines the context and most important: the repositories. The workspace is by default defined in the workspace's cnf directory.
The -standalone instruction tells bnd that this connection should be severed and that all information is contained in the bndrun file. The value of the -standalone instruction is used to define the repositories. Each repo-spec clause defines a repository.
-standalone ::= repo-spec ( ',' repo-spec )*
+repo-spec ::= <relative url> ( '; <fixed index repo attrs> )*
+The repositories that are created from the -standalone instruction are the OSGi Capabilities repositories as implemented by the Fixed Index Repository. For this repository you can add the following attributes:
name – Name of the repositorycache – File path to a cache directorytimeout – Time out for downloading the index-standalone: index.html; name=local
+-runfw: org.apache.felix.framework;version=5
+-runee: JavaSE-1.8
+-runrequires: osgi.identity;filter:='(osgi.identity=com.springsource.org.apache.tools.ant)'
+Example: -strict=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -strict instruction enables extra verification during the build process. When set to true, bnd performs additional checks and validation to ensure the highest quality and correctness of the bundle.
Example:
+-strict: true
+This is useful for enforcing stricter build requirements and catching potential issues early.
+TODO Needs review - AI Generated content
+Example: -sub=com.acme.*.bnd
Pattern: .*
You can enable sub-bundles with the -sub instruction, to build a set of .bnd files that use the bnd.bnd file as a basis. The list of sub-.bnd files can be specified with wildcards.
Assume a bundle com.example with a main bnd.bnd file:
-sub *.bnd
And besides the following files:
+sub.bundle1.bndsub.bundle2.bndThis will process every other .bnd file as a sub-bundle and will create a .jar file for it:
generated/com.example.sub.bundle1.jargenerated/com.example.sub.bundle2.jarThe Bundle Symbolic Name for each sub bundle will be the name of the project + . + file name prefix of the sub bundle.
Sub bundles are mostly treated as independent projects. They are part of the repositories and release process.
+The content of the sub .bnd files is as usual (see also Generating JARs).
The -buildpath is a project instruction. This means that it must be placed in the main bnd.bnd file, not in the sub-.bnd file. A project can only have a single -buildpath.
Thus, in the sub bundle .bnd-files you have to use -classpath instead.
Example:
+-classpath: \
+ ${repo;org.apache.poi:poi;latest},\
+A case where this approach is useful is, when you provide an implementation and want to separate the Gogo commands from the main code. In that case you create a main provider bundle and a Gogo bundle.
+In most cases it is best to have one project generate one bundle. However, sometimes it creates a lot of overhead and you’d like to be able to provide multiple bundles in a project since they share so much information.
+Another use case is when you need to wrap .jar files, e.g. to convert non-OSGi bundles into OSGi-Bundles by adding meta-data to the Manifest.
+A practise to do so could be to have a single Wrapper-bundle where you do all your wrapping of external dependencies. The advantage is, that you don't clutter up your workspace with lots of tiny Bundle-wrapping projects. With sub-bundles you can keep all your wrapped jars in one single project.
Example: -systemproperties= foo=3, bar=4
Pattern: .*
The -systemproperties instruction allows you to set system properties in the local JVM when a workspace is started. These properties are applied as early as possible, making it possible to configure JVM options or work around specific Java issues (such as SSL settings) before any other code runs.
This is especially useful for setting environment-specific options or JVM flags that must be in place for your workspace or build to function correctly.
+TODO Needs review - AI Generated content
+Example: -testcontinuous=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -testcontinuous instruction enables continuous testing for your project. When set to true, bnd will not exit after running the test suites. Instead, it will keep watching the bundles and automatically rerun the test cases if any bundle is updated.
This is useful for development workflows where you want immediate feedback on changes, as tests will be re-executed whenever relevant code is modified.
+TODO Needs review - AI Generated content
+Example: -tester=biz.aQute.tester;version=latest
Pattern: .*
The -tester instruction defines what bundle is used to setup testing. This bundle must have a Tester-Plugin that will setup the test environment.
By default the, -tester is the bundle biz.aQute.tester. This bundle will instruct bnd to add this bundle to the -runbundles, sets the appropriate properties, and then launches. It will then run whatever tests are configured.
For a long time bnd had biz.aQute.junit as the tester. This launcher added itself to the -runpath and then executed the tests from there. Unfortunately this required that the tester actually exported the JUnit packages. This caused constraints between JUnit and bnd that was not good because JUnit itself is not directly a shining example of software engineering :-(
If you want to be backward compatible with the older model, set:
+-tester: biz.aQute.junit
+Example: -testpackages=test;presence:=optional
Pattern: .*
The -testpackages instruction allows you to specify which packages should be included as test packages in your project. You provide a comma-separated list of package specifications, and bnd will treat these packages as containing test code.
This is useful for projects that separate test code into specific packages or want to control which packages are considered for testing, especially when running or packaging tests.
+TODO Needs review - AI Generated content
+Example: -testpath=som.bundle.symbolicname;version=latest
Pattern: .*
The -testpath instruction specifies JARs from a repository that should be added to the remote JVM's classpath when the JVM is started in test mode. These JARs are included in addition to those specified by -runpath and are typically used to provide test dependencies, such as JUnit or other testing libraries.
This is useful for ensuring that all necessary test dependencies are available during test execution without including them in the main runtime classpath.
+Example: -testsources=*.java
Values: REGEX ( ',' REGEX )*
Pattern: .*
The -testsources instruction specifies how bnd should find JUnit test cases by searching the test source directory for Java classes. By default, it looks for all .java files, but you can provide a regular expression to customize which files are considered test sources.
This instruction is useful for projects with non-standard test file naming or organization, allowing you to control which files are included as test cases during the build process.
+Example: -testunresolved=true
Values: true,false
Pattern: true|false|TRUE|FALSE
The -testunresolved instruction controls whether a special JUnit test case is executed before any other test cases to check for unresolved bundles. If any bundles are unresolved, this test will abort the test run and report the issue as a failure.
This is useful for ensuring that your test environment is fully resolved before running tests, helping to catch dependency or configuration problems early in the process.
+true|falseThe -undertest instruction is set by the project when it builds a JAR in test mode. It is intended to be used by plugins or build logic to indicate that the current build is for testing purposes. When this instruction is present, certain behaviors or configurations (such as including test packages) may be enabled automatically.
When building with -undertest, the build process skips dependency checking, so it is important to ensure that any dependent projects are built first. The instruction also causes the project to include test-specific packages (such as those specified by -testpackages) in the build, making them available for testing. This helps automate and streamline the test build process, ensuring that test-specific settings are applied only when needed and that the resulting JAR is suitable for test execution.
In summary, -undertest signals that the current build is for testing, enables test package inclusion, and skips dependency checks to speed up the test build workflow.
Example: -upto: 2.3.1
Pattern: (\d{1,10})(\.(\d{1,10})(\.(\d{1,10})(\.([-\w]+))?)?)?
The -upto instruction specifies the highest version of bnd features that your project should be compatible with. When you set this instruction, any features or behaviors introduced after the specified version will be disabled, ensuring compatibility with earlier versions of bnd.
This is useful for maintaining backward compatibility or for projects that need to avoid adopting new behaviors automatically. If -upto is not set, bnd assumes the latest version and enables all features by default.
In summary, use -upto to control the introduction of new features and maintain a stable build environment as bnd evolves.
Example: -wab=static-pages/
Pattern: .*
In OSGi Enterprise 4.2 the concept of Web Archive Bundles were introduced. Web Archive Bundles are 100% normal bundles following all the rules of OSGi. Their speciality is that they can be mapped to a web server following several of the rules of Java Enterprise Edition's Servlet model. The big difference is that the WARs of the servlet model have a rather strict layout of their archive because the servlet container also handles class loading. In OSGi, the class loading is very well specified and it would therefore be wrong to create special rules.
+However, the OSGi supports the Bundle-Classpath header. This header allows the organization of the internal layout. It turns out that it is possible to create a valid Web Application Bundle (WAB) that is also a valid Web ARchive (WAR). Being deploy an archive both to OSGi and an application server obviously has advantages. bnd therefore supports a number of instructions that make it easy to create these dual mode archives.
+The -wab instruction instructs bnd to move the root of the created archive to WEB-INF/classes. That is, you build your bundle in the normal way,not using the Bundle-ClassPath. The -wab command then moves the root of the archive so the complete class path for the bundle is no inside the WEB-INF/classes directory. It then adjusts the Bundle-ClassPath header to reflect this new location of the classes and resources.
The new root now only contains WEB-INF. In the Servlet specification, the root of the archive is mapped to the server's context URL. It is therefore often necessary to place static files in the root. For this reason, the -wab instruction has the same form as Include-Resource header, and performs the same function. However, it performs this function of copying resources from the file system after the classes and resources of the original bundle have been moved to WEB-INF.
For example, the following code creates a simple WAB/WAR:
+ Private-Package: com.example.impl.*
+ Export-Package: com.example.service.myapi
+ Include-Resource: resources/
+ -wab: static-pages/
+The layout of the resulting archive is:
+ WEB-INF/classes/com/example/impl/
+ WEB-INF/classes/com/example/service/myapi/
+ WEB-INF/classes/resources/
+ index.html // from static-pages
+The Bundle-ClassPath is WEB-INF/classes.
WARs can carry a WEB-INF/lib directory. Any archive in this directory is mapped to the class path of the WAR. The OSGi specifications do not recognize directories with archives it is therefore necessary to list these archives also on the Bundle-ClassPath header. This is cumbersome to do by hand so the -wablib command will take a list of paths.
Private-Package: com.example.impl.*
+ Export-Package: com.example.service.myapi
+ Include-Resource: resources/
+ -wab: static-pages/
+ -wablib: lib/a.jar, lib/b.jar
+This results in a layout of:
+ WEB-INF/classes/com/example/impl/
+ WEB-INF/classes/com/example/service/myapi/
+ WEB-INF/classes/resources/
+ WEB-INF/lib/
+ a.jar
+ b.jar
+ index.html ( from static-pages)
+The Bundle-ClassPath is now set to WEB-INF/classes,WEB-INF/lib/a.jar,WEB-INF/lib/a.jar
/**
+ * Turn this normal bundle in a web and add any resources.
+ *
+ * @throws Exception
+ */
+ private Jar doWab(Jar dot) throws Exception {
+ String wab = getProperty(WAB);
+ String wablib = getProperty(WABLIB);
+ if (wab == null && wablib == null)
+ return dot;
+
+ trace("wab %s %s", wab, wablib);
+ setBundleClasspath(append("WEB-INF/classes", getProperty(BUNDLE_CLASSPATH)));
+
+ Set<String> paths = new HashSet<String>(dot.getResources().keySet());
+
+ for (String path : paths) {
+ if (path.indexOf('/') > 0 && !Character.isUpperCase(path.charAt(0))) {
+ trace("wab: moving: %s", path);
+ dot.rename(path, "WEB-INF/classes/" + path);
+ }
+ }
+
+ Parameters clauses = parseHeader(getProperty(WABLIB));
+ for (String key : clauses.keySet()) {
+ File f = getFile(key);
+ addWabLib(dot, f);
+ }
+ doIncludeResource(dot, wab);
+ return dot;
+ }
+Example: -wablib=lib/a.jar, lib/b.jar
Pattern: .*
The -wablib instruction specifies additional libraries (JAR files) that should be included in a Web Archive Bundle (WAB) or WAR. These libraries are added to the WEB-INF/lib directory of the resulting archive, making them available to the web application at runtime.
This instruction is useful when you need to package extra dependencies with your web bundle, ensuring that all required libraries are present in the deployed artifact.
+Example: -workingset=Implementations, Drivers
Pattern: .*
A workingset is a name for a group of projects that have something in common. You can tag a project for one or more workingsets using the -workingset instruction in the bnd.bnd file. For example, to make a project member of the Implementations and Drivers working sets you can enter the following in a bnd file:
-workingset Implementations, Drivers
+An IDE can use this information to provide a grouping to projects. However, this information can also be used to tag bundles in the Manifest, like:
+Bundle-Category ${-workingset.*}
+The syntax for the instruction is:
+workingset := '-workingset' tag ( ',' tag )*
+tag := NAME ( ';member=' TRUTHY )?
+NAME := <JavaIdentifierPart+>
+The instruction is a merged instruction so you can also set:
+-workingset.all All
+Note that, the name of the working set must use the pattern of Java identifier.
+As you could see in the syntax, you can also specify a member attribute on the tag. This member attribute evaluates to a truthy. A truthy is true if it is not empty, 0, nor false. The truthy is well supported by macros so you can now use the bnd macro language to decide if a project should be a member of a working set or not. This can be used in many ways. For example, you could use it to do name matching. By placing the following in the cnf/build.bnd file you do not have to place the -workingset instruction in each bnd file.
-workingset = \
+ impl;member=${filter;${p};.*\.provider}, \
+ api;member=${filter;${p};.*\.api}, \
+ test;member=${filter;${p};.*\.test}
+The feature will create working sets as demanded but will reuse existing working set with the matching name. If no -workingset instruction is given, the working sets are not touched in any way for that project. That is, they are then not removed from existing sets.
In some cases it is necessary to maintain a working set manual. Such a workingset is then stored in Eclipse and not shared with the team. To
+create a manual working set, use a name that is outside the specified NAME pattern. For example, use a name that starts with a
+dot (.) like .Private. Since you cannot use these names in the -workingset instruction (they generate an error)
+bnd will never look at workingsets with such a name.
.*The -workspace-templates instruction allows you to define templates for creating new workspaces. These templates can be referenced when initializing a workspace to ensure consistent structure and configuration.
See workspace templates for more details and usage examples.
+Example: -x-overwritestrategy=gc
Values: (classic|delay|gc|windows-only-disposable-names|disposable-names)
Pattern: .*
Convert comma-separated lists into semicolon-separated macro arguments, effectively "applying" list elements as individual macro parameters.
+${apply;<macro-name>[;<list>...]}
+${})The macro: +1. Takes the specified macro name +2. Splits all provided lists into individual elements (using comma separation) +3. Constructs a new macro invocation with elements as semicolon-separated arguments +4. Expands the constructed macro
+This is useful when you have arguments in a comma-separated list but need to pass them as separate parameters to a macro.
+# Basic example - convert list to macro arguments
+args = com.example.foo, 3.12, HIGHEST
+${apply;repo;${args}}
+# Expands to: ${repo;com.example.foo;3.12;HIGHEST}
+
+# Apply multiple lists
+list1 = a, b
+list2 = c, d
+${apply;mymacro;${list1};${list2}}
+# Expands to: ${mymacro;a;b;c;d}
+
+# Use with filter macro
+packages = com.example.*, org.sample.*, com.test.*
+pattern = com\..*
+${apply;filter;${packages};${pattern}}
+# Expands to: ${filter;com.example.*,org.sample.*,com.test.*;com\..*}
+
+# Repository lookup with dynamic arguments
+repo-args = my.bundle, 1.0.0, HIGHEST
+bundle-path = ${apply;repo;${repo-args}}
+
+# Use with format macro
+format-string = %s-%s-%s
+values = group, artifact, version
+${apply;format;${format-string};${values}}
+# Expands to: ${format;%s-%s-%s;group;artifact;version}
+
+# Complex example with multiple lists
+deps1 = foo, bar
+deps2 = baz
+${apply;join;,;${deps1};${deps2}}
+# Expands to: ${join;,;foo;bar;baz}
+${} delimitersSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/average.html b/org.bndtools.help/docs/macros/average.html new file mode 100644 index 0000000000..649d40a9fd --- /dev/null +++ b/org.bndtools.help/docs/macros/average.html @@ -0,0 +1,53 @@ + + + + +The average macro calculates the arithmetic mean of all numeric values provided in one or more semicolon-separated lists. Each list element is parsed as a double-precision floating-point number, and the result is the sum of all values divided by the count.
${average;<list>[;<list>...]}
+list - One or more semicolon-separated lists of numeric values. Values within each list can be comma-separated.Calculate average of a simple list:
+${average;1,2,3,4,5}
+# Returns: 3
+Calculate average across multiple lists:
+${average;10,20,30;40,50}
+# Returns: 30 (average of 10,20,30,40,50)
+Calculate average of decimal values:
+${average;1.5,2.5,3.5}
+# Returns: 2.5
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/base64.html b/org.bndtools.help/docs/macros/base64.html new file mode 100644 index 0000000000..4b14bcf5cf --- /dev/null +++ b/org.bndtools.help/docs/macros/base64.html @@ -0,0 +1,59 @@ + + + + +The base64 macro reads a file and returns its contents encoded as a Base64 string. This is useful for embedding binary content or file data directly in text-based configuration files.
${base64;<file>[;fileSizeLimit]}
+file - Path to the file to encode (relative to the project directory)fileSizeLimit (optional) - Maximum file size in bytes (default: 100,000 bytes)Encode a small binary file:
+${base64;resources/icon.png}
+Encode with custom size limit (1MB):
+${base64;data/config.bin;1000000}
+Embed a certificate in a property:
+certificate.data=${base64;certs/ca-cert.pem}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/basedir.html b/org.bndtools.help/docs/macros/basedir.html new file mode 100644 index 0000000000..cb0a71683c --- /dev/null +++ b/org.bndtools.help/docs/macros/basedir.html @@ -0,0 +1,54 @@ + + + + +The basedir macro returns the absolute path to the base directory of the current processor (project, workspace, or build context). This is the root directory from which relative paths are resolved.
${basedir}
+None - this macro takes no parameters.
+Get the current project's base directory:
+project.root=${basedir}
+Build a path relative to the base directory:
+resources.dir=${basedir}/src/main/resources
+Reference files using the base directory:
+Bundle-Icon: ${basedir}/icon.png
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/basename.html b/org.bndtools.help/docs/macros/basename.html new file mode 100644 index 0000000000..d87261fa35 --- /dev/null +++ b/org.bndtools.help/docs/macros/basename.html @@ -0,0 +1,71 @@ + + + + +The basename macro extracts the filename (last component) from one or more file paths. It returns only the filenames for files that exist.
${basename;<filepath>[;<filepath>...]}
+filepath - One or more file paths (relative to project directory)Get single filename:
+${basename;/path/to/file.txt}
+# Returns: "file.txt"
+Multiple files:
+${basename;src/Main.java;src/Util.java}
+# Returns: "Main.java,Util.java"
+With project-relative paths:
+${basename;${@}}
+# Returns filename of current bundle JAR
+Extract from full paths:
+${basename;${buildpath}}
+# Returns basenames of all buildpath entries
+${dir} for directory path${stem} for filename without extensionSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/basenameext.html b/org.bndtools.help/docs/macros/basenameext.html new file mode 100644 index 0000000000..56e9df5dfe --- /dev/null +++ b/org.bndtools.help/docs/macros/basenameext.html @@ -0,0 +1,25 @@ + + + + +Returns the basename of the specified path optionally minus a specified extension. This is the last segment of the path. If an extension is specified, the basename is examined for a . separating the extension from the rest of the file name. If the extension of the basename matches the specified extension, this extension is removed from the basename before it is returned. The extension, if specified, may optionally start with ..
# returns 'abcdef.def'
+${basenameext;abcdef.def}
+${basenameext;/foo.bar/abcdef.def}
+${basenameext;abcdef.def;bar}
+${basenameext;/foo.bar/abcdef.def;bar}
+
+# returns 'abcdef'
+${basenameext;abcdef.def;def}
+${basenameext;/foo.bar/abcdef.def;def}
+${basenameext;abcdef.def;.def}
+${basenameext;/foo.bar/abcdef.def;.def}
+Returns the version number of the bnd tool currently being used to process the build.
+${bndversion}
+None. This macro takes no parameters.
+Returns the current bnd version as a string in the format major.minor.micro (without the qualifier/build timestamp).
For example: 7.0.0 or 6.4.0 or 5.3.0
# Display bnd version in bundle manifest
+Bundle-BuildTool: bnd-${bndversion}
+
+# Conditional logic based on bnd version
+-include ${if;${vcompare;${bndversion};7.0.0};modern.bnd;legacy.bnd}
+
+# Document which bnd version was used
+Bundle-Comment: Built with bnd ${bndversion}
+
+# Version-specific features
+-conditionalpackage: ${if;${vcompare;${bndversion};6.4.0};com.new.*;com.legacy.*}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/bsn.html b/org.bndtools.help/docs/macros/bsn.html new file mode 100644 index 0000000000..06cc190961 --- /dev/null +++ b/org.bndtools.help/docs/macros/bsn.html @@ -0,0 +1,56 @@ + + + + +The bsn macro returns the Bundle Symbolic Name (BSN) of the bundle currently being analyzed or built. This is particularly useful in sub-bundle scenarios where the BSN may differ from the project's main BSN.
${bsn}
+None - this macro takes no parameters.
+Use the BSN in a manifest header:
+Bundle-Description: Bundle for ${bsn}
+Create BSN-based paths:
+output.file=${bsn}.jar
+Reference in bundle documentation:
+Bundle-DocURL: https://example.com/docs/${bsn}
+${project.name} which is the project name.bnd files), each sub-bundle has its own BSNSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/bytes.html b/org.bndtools.help/docs/macros/bytes.html new file mode 100644 index 0000000000..0060fb4ddd --- /dev/null +++ b/org.bndtools.help/docs/macros/bytes.html @@ -0,0 +1,67 @@ + + + + +The bytes macro converts a byte count (as long integer) into human-readable format with appropriate size units (b, Kb, Mb, Gb, etc.). It automatically selects the most appropriate unit by dividing by 1024 until the value is less than 1024.
+Supports formatting a list of inputs.
${bytes;<number>*}
+number - One or multiple numeric values representing byte counts (as long integers)Format a small byte count:
+${bytes;1024}
+# Returns: "1.0 Kb"
+Format megabytes:
+${bytes;5242880}
+# Returns: "5.0 Mb" (5 * 1024 * 1024 bytes)
+Format gigabytes:
+${bytes;10737418240}
+# Returns: "10.0 Gb"
+Format multiple
+${bytes;1048576;10000048576}
+# Returns: "1.0 Mb 9.3 Gb"
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/cat.html b/org.bndtools.help/docs/macros/cat.html new file mode 100644 index 0000000000..44374801f0 --- /dev/null +++ b/org.bndtools.help/docs/macros/cat.html @@ -0,0 +1,69 @@ + + + + +The cat macro reads the contents of a file, directory, or URL and returns it as a string. It's similar to the Unix cat command, allowing you to embed file contents directly in your configuration.
${cat;<path-or-url>}
+path-or-url - Path to a file or directory (relative to project), or a URLRead a text file:
+license.text=${cat;LICENSE.txt}
+Read a property file:
+build.info=${cat;build.properties}
+List directory contents:
+resource.files=${cat;resources}
+Fetch content from a URL:
+remote.config=${cat;https://example.com/config.txt}
+Embed a template:
+header=${cat;templates/header.txt}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/classes.html b/org.bndtools.help/docs/macros/classes.html new file mode 100644 index 0000000000..2595da4e14 --- /dev/null +++ b/org.bndtools.help/docs/macros/classes.html @@ -0,0 +1,127 @@ + + + + +The classes macro provides a query function in an analyzed bundle. While analyzing, the Analyzer stores each found class on the Bundle-Classpath with some key information. A simple query language is used to query this dictionary. For example, if you want to make a manifest header with all public classes in the bundle:
+Public-Classes: ${classes;PUBLIC}
+The query language is conjunctive, that is, all entries form an AND. For example, if you want to find all PUBLIC classes that are also not abstract you would do:
+PublicConcrete-Classes: ${classes;PUBLIC;CONCRETE}
+Some query types can also take parameters. This is a pattern that must match some aspect of the class. For example, it is possible to query for classes that extend a certain base class or is annotated by a certain annotation type:
+Test-Cases: ${classes;CONCRETE;EXTENDS;junit.framework.TestCase}
+Test-Cases: ${classes;CONCRETE;HIERARCHY_ANNOTATED;org.junit.Test}
+Test-Cases: ${classes;CONCRETE;HIERARCHY_INDIRECTLY_ANNOTATED;org.junit.platform.commons.annotation.Testable}
+All pattern matching is based on fully qualified name and uses the globbing model.
+The following table specifies what query options there are:
+| Query | +Parameter | +Description | +
|---|---|---|
| ANY | ++ | Matches any class | +
| CONCRETE | ++ | Class must not be abstract. | +
| ABSTRACT | ++ | Class must be abstract. | +
| PUBLIC | ++ | Class must be public. | +
| STATIC | ++ | Class must be explicitly or implicitly declared static. That is, the class must not be a inner class. | +
| INNER | ++ | Class must be an inner class. That is, the class must be a nested class that is not explicitly or implicitly declared static. Inner classes include anonymous and local classes. | +
| CLASSANNOTATIONS | ++ | The class must have some CLASS retention annotations. | +
| RUNTIMEANNOTATIONS | ++ | The class must have some RUNTIME retention annotations. | +
| DEFAULT_CONSTRUCTOR | ++ | The class must have a default constructor. That is, the class must have a public, no-argument constructor. | +
| IMPLEMENTS | +PATTERN | +The class must implement at least one fully qualified interface name that matches the given pattern. This takes inheritance into account as long as super types can be found on the classpath. This query uses the Java source code fully qualified name where `.` (not `$`) separates the nested class name from the outer class name. | +
| EXTENDS | +PATTERN | +The class must extend at least one fully qualified class name that matches the given pattern. This takes inheritance into account as long as super types can be found on the classpath. This query uses the Java source code fully qualified name where `.` (not `$`) separates the nested class name from the outer class name. | +
| IMPORTS | +PATTERN | +The class must use a type from another package name that matches the given pattern | +
| NAMED | +PATTERN | +The fully qualified name of the class must match the given pattern. This query uses the Java source code fully qualified name where `.` (not `$`) separates the nested class name from the outer class name. | +
| VERSION | +PATTERN | +The class format of the given class must match the given version. The version is given as "major.minor", like "49.0". To select classes that are Java 6, do ${classes;VERSION;49.*} |
+
| ANNOTATED | +PATTERN | +The class must be directly annotated with a fully qualified annotation name that matches the pattern. The set of annotations is all annotations in the class, also the annotations on fields and methods. This query uses the Java class name fully qualified name where `$` (not `.`) separates the nested class name from the outer class name. | +
| INDIRECTLY_ANNOTATED | +PATTERN | +The class must be directly or indirectly annotated with a fully qualified annotation name that matches the pattern. The set of annotations is all annotations in the class, the annotations on fields and methods, and all the annotations on those annotations recursively. This query uses the Java class name fully qualified name where `$` (not `.`) separates the nested class name from the outer class name. | +
| HIERARCHY_ANNOTATED | +PATTERN | +The class, or one of its super classes, must be directly annotated with a fully qualified annotation name that matches the pattern. The set of annotations is all annotations in the class, also the annotations on fields and methods. This query uses the Java class name fully qualified name where `$` (not `.`) separates the nested class name from the outer class name. | +
| HIERARCHY_INDIRECTLY_ANNOTATED | +PATTERN | +The class, or one of its super classes, must be directly or indirectly annotated with a fully qualified annotation name that matches the pattern. The set of annotations is all annotations in the class, the annotations on fields and methods, and all the annotations on those annotations recursively. This query uses the Java class name fully qualified name where `$` (not `.`) separates the nested class name from the outer class name. | +
bnd will attempt to use the resources on the classpath if a super class or interface that is referenced from an analyzed class is not in the class space. However, bnd does not require that all dependencies are available on the classpath. In such a case it is not possible to do a complete analysis. For example, if A extends B and B extends C then it can only be determined that A extends C if B can be analyzed.
+ + diff --git a/org.bndtools.help/docs/macros/compare.html b/org.bndtools.help/docs/macros/compare.html new file mode 100644 index 0000000000..d0d664c723 --- /dev/null +++ b/org.bndtools.help/docs/macros/compare.html @@ -0,0 +1,82 @@ + + + + +Compare two strings lexicographically using Java's String.compareTo() method, returning -1, 0, or 1.
${compare;<string1>;<string2>}
+Compares the two strings lexicographically (alphabetically) and returns:
+- 0 - The strings are equal
+- 1 - The first string is greater than the second (comes later alphabetically)
+- -1 - The first string is less than the second (comes earlier alphabetically)
The comparison is case-sensitive and uses Unicode character values.
+# Equal strings
+${compare;hello;hello}
+# Returns: 0
+
+# First string greater
+${compare;world;hello}
+# Returns: 1
+
+# First string less
+${compare;apple;banana}
+# Returns: -1
+
+# Case-sensitive comparison
+${compare;Apple;apple}
+# Returns: -1 (uppercase comes before lowercase)
+
+# Use in conditional logic
+-include ${if;${compare;${env;MODE};production};prod.bnd;dev.bnd}
+
+# Numeric strings (lexicographic, not numeric)
+${compare;10;2}
+# Returns: -1 (because "1" < "2" alphabetically)
+
+# Empty string comparison
+${compare;;text}
+# Returns: -1 (empty string is less than any non-empty string)
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/currenttime.html b/org.bndtools.help/docs/macros/currenttime.html new file mode 100644 index 0000000000..30b5425a26 --- /dev/null +++ b/org.bndtools.help/docs/macros/currenttime.html @@ -0,0 +1,62 @@ + + + + +The currenttime macro returns the current system time as a long integer representing milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC). This provides a precise timestamp for build-time operations.
${currenttime}
+None - this macro takes no parameters.
+System.currentTimeMillis() internallyCapture build time:
+Build-Time: ${currenttime}
+Use as a unique identifier component:
+Build-Id: ${bsn}-${currenttime}
+Create timestamped filenames:
+output.file=bundle-${currenttime}.jar
+Calculate elapsed time (with another timestamp):
+# In combination with other macros for time calculations
+${long2date} macro${tstamp} macro instead${now} which returns seconds since epochSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/decorated.html b/org.bndtools.help/docs/macros/decorated.html new file mode 100644 index 0000000000..b6d4f20518 --- /dev/null +++ b/org.bndtools.help/docs/macros/decorated.html @@ -0,0 +1,103 @@ + + + + +Get a merged and decorated Parameters object, where decoration allows pattern-based attribute addition to parameter entries.
+${decorated;<property-name>[;<include-literals>]}
+false.The macro:
+1. Merges all properties starting with the given name (including .extra suffixes)
+2. Decorates entries by matching them against decorator patterns from properties ending with +
+3. Returns the merged and decorated Parameters as a string
A "decorator" property (name ending with +) contains glob patterns as keys:
+- Patterns are matched against keys from the merged parameters
+- Matching entries receive additional attributes from the decorator
+- If include-literals is true, unmatched decorator entries are also included
# Basic decoration
+parameters = a, b
+parameters.extra = c, d
+parameters+ = (c|d);attr=X
+${decorated;parameters}
+# Returns: a,b,c;attr=X,d;attr=X
+
+# Include literals
+parameters = a, b
+parameters.extra = c, d
+parameters+ = (c|d);attr=X, x, y, z
+${decorated;parameters;true}
+# Returns: a,b,c;attr=X,d;attr=X,x,y,z
+# (x,y,z are literals from decorator, included because second arg is true)
+
+# Package decoration
+-exportpackage = com.example.api, com.example.util
+-exportpackage.internal = com.example.internal
+-exportpackage+ = *.internal;version=0.0.0;x-internal:=true
+Export-Package: ${decorated;-exportpackage}
+# internal package gets version and x-internal attributes
+
+# Build path decoration
+-buildpath = commons-io, commons-lang
+-buildpath.test = junit, mockito
+-buildpath+ = junit;version=4.12, mockito;version=2.0
+${decorated;-buildpath}
+# Test dependencies get specific versions
+
+# Conditional attributes
+packages = com.example.*, org.sample.*
+packages+ = com.example.*;export=true
+packages+ = org.sample.*;export=false
+${decorated;packages}
+
+# Multiple decorators
+deps = foo, bar, baz
+deps+ = foo;version=1.0
+deps+ = bar;version=2.0
+${decorated;deps}
+# Returns: foo;version=1.0,bar;version=2.0,baz
+.extra suffixes)+ and use glob patterns for matching* (any chars), ? (one char), [abc] (char class)include-literals is false (default), only matched patterns contributeinclude-literals is true, all decorator entries are includedSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/def.html b/org.bndtools.help/docs/macros/def.html new file mode 100644 index 0000000000..9db66a5aa9 --- /dev/null +++ b/org.bndtools.help/docs/macros/def.html @@ -0,0 +1,68 @@ + + + + +The def macro looks up a property by key and returns its value, or returns a default value if the property is not set. The default is an empty string if not specified.
${def;<key>[;<default>]}
+key - The property name to look updefault (optional) - Value to return if property not set (default: empty string)Get property with default:
+${def;version;1.0.0}
+# Returns property value or "1.0.0" if not set
+Empty default:
+${def;optional.setting}
+# Returns property value or "" if not set
+Use in paths:
+output.dir=${def;custom.dir;target}
+Conditional with default:
+${if;${def;debug.mode;false};debug-on;debug-off}
+${key}, returns default instead of literal${get} for list element access${if} for conditional defaults See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/digest.html b/org.bndtools.help/docs/macros/digest.html new file mode 100644 index 0000000000..12eeefb7c4 --- /dev/null +++ b/org.bndtools.help/docs/macros/digest.html @@ -0,0 +1,69 @@ + + + + +The digest macro computes a cryptographic hash of a file using a specified algorithm (such as MD5, SHA-1, SHA-256, etc.). The result is returned as a hexadecimal string.
${digest;<algorithm>;<file>}
+algorithm - The digest algorithm name (e.g., "MD5", "SHA-1", "SHA-256", "SHA-512")file - Path to the file to hash (relative to project directory)Calculate MD5 hash:
+file.md5=${digest;MD5;target/mybundle.jar}
+Calculate SHA-256 hash:
+file.sha256=${digest;SHA-256;LICENSE.txt}
+Calculate SHA-512 for verification:
+Bundle-Digest-SHA512: ${digest;SHA-512;${@}}
+Multiple checksums:
+md5.checksum=${digest;MD5;artifact.jar}
+sha1.checksum=${digest;SHA-1;artifact.jar}
+sha256.checksum=${digest;SHA-256;artifact.jar}
+${md5} and ${sha1} macros for specific algorithmsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/dir.html b/org.bndtools.help/docs/macros/dir.html new file mode 100644 index 0000000000..c61476edff --- /dev/null +++ b/org.bndtools.help/docs/macros/dir.html @@ -0,0 +1,69 @@ + + + + +The dir macro extracts and returns the parent directory path from one or more file paths. It returns the absolute path to each file's containing directory, filtering out non-existent files.
${dir;<filepath>[;<filepath>...]}
+filepath - One or more file paths (relative to project directory)Get directory of a single file:
+source.dir=${dir;src/main/java/Example.java}
+# Returns: /path/to/project/src/main/java
+Get directories of multiple files:
+dirs=${dir;LICENSE.txt;README.md}
+# Returns: /path/to/project,/path/to/project
+Use in path construction:
+parent.dir=${dir;${@}}
+# Gets directory containing current bundle JAR
+Build relative path:
+Bundle-License: ${dir;LICENSE}/LICENSE.txt
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/driver.html b/org.bndtools.help/docs/macros/driver.html new file mode 100644 index 0000000000..d8a7278225 --- /dev/null +++ b/org.bndtools.help/docs/macros/driver.html @@ -0,0 +1,71 @@ + + + + +The driver macro identifies the build environment (driver) that is running bnd, such as Gradle, Ant, Eclipse, or IntelliJ. It can be used to return the driver name or check if the current driver matches specific names.
The driver should be set when bnd is started by the build tool and can be overridden with the -bnd-driver property. This is useful for avoiding target-dir conflicts between different build tools.
${driver[;<name>...]}
+name (optional) - One or more driver names to check against. If provided, returns the driver name only if it matches one of the arguments; otherwise returns empty string.Workspace.setDriver() or -bnd-driver propertyGet current driver:
+${driver}
+# Returns: "gradle" (if running under Gradle)
+Check if running under Gradle:
+${if;${driver;gradle};gradle-specific-config;}
+Check multiple drivers:
+${driver;gradle;maven}
+# Returns: "gradle" if gradle, or "maven" if maven, otherwise ""
+Conditional configuration:
+-target-dir: ${if;${driver;gradle};gradle-build;${if;${driver;eclipse};eclipse-build;build}}
+Set via property:
+-bnd-driver: custom-driver
+-bnd-driver propertySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/ee.html b/org.bndtools.help/docs/macros/ee.html new file mode 100644 index 0000000000..134bae714f --- /dev/null +++ b/org.bndtools.help/docs/macros/ee.html @@ -0,0 +1,58 @@ + + + + +The ee macro returns the name of the highest Java Execution Environment (EE) required by the classes in the current bundle being analyzed. This is determined by analyzing the bytecode version of all classes in the JAR.
${ee}
+None - this macro takes no parameters.
+Set the Bundle-RequiredExecutionEnvironment based on analyzed classes:
+Bundle-RequiredExecutionEnvironment: ${ee}
+Use in conditional logic:
+# Check if Java 11+ is required
+java11plus=${if;${vcompare;${ee};JavaSE-11};true;false}
+Document the requirement:
+Bundle-Description: Requires ${ee} or higher
+Bundle-RequiredExecutionEnvironment headerSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/endswith.html b/org.bndtools.help/docs/macros/endswith.html new file mode 100644 index 0000000000..de684a41ae --- /dev/null +++ b/org.bndtools.help/docs/macros/endswith.html @@ -0,0 +1,72 @@ + + + + +The endswith macro checks if a given string ends with a specified suffix. If the string ends with the suffix, it returns the string; otherwise, it returns an empty string (which evaluates to false in conditional contexts).
${endswith;<string>;<suffix>}
+string - The string to checksuffix - The suffix to look for at the end of the stringCheck if a filename ends with .jar:
+${endswith;mybundle.jar;.jar}
+# Returns: "mybundle.jar"
+Check if a string ends with specific text:
+${endswith;com.example.api;.api}
+# Returns: "com.example.api"
+Use in conditional logic:
+${if;${endswith;${project};.test};test-project;regular-project}
+# Checks if project name ends with ".test"
+Filter files with specific extension:
+jar.file=${if;${endswith;${@};.jar};${@};}
+Check version suffix:
+${if;${endswith;${version};-SNAPSHOT};snapshot;release}
+${if} macro for conditional logic${startswith} for checking string prefixesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/env.html b/org.bndtools.help/docs/macros/env.html new file mode 100644 index 0000000000..dc86d7ca0d --- /dev/null +++ b/org.bndtools.help/docs/macros/env.html @@ -0,0 +1,69 @@ + + + + +The env macro looks up an environment variable and returns its value, or returns a default value if the variable is not set. The default is an empty string if not specified.
${env;<variable>[;<default>]}
+variable - The environment variable name to look updefault (optional) - Value to return if variable not set (default: empty string)System.getenv()Get environment variable with default:
+${env;JAVA_HOME;/usr/lib/jvm/default}
+Check if variable exists:
+${if;${env;CI};running-in-ci;local-build}
+Use in paths:
+user.home=${env;HOME;/home/default}
+Empty default:
+${env;OPTIONAL_VAR}
+# Returns value or "" if not set
+${def} for properties${system} for system commands See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/error.html b/org.bndtools.help/docs/macros/error.html new file mode 100644 index 0000000000..099f3caa8f --- /dev/null +++ b/org.bndtools.help/docs/macros/error.html @@ -0,0 +1,70 @@ + + + + +The error macro generates one or more build errors with custom messages. Each message argument is processed (macros are expanded) and then added to the build error list. This causes the build to fail.
${error;<message>[;<message>...]}
+message - One or more error messages to generate. Each message can contain macros that will be expanded.Generate a simple error:
+${error;This configuration is not supported}
+Generate error with variable substitution:
+${error;Invalid version: ${version}}
+Conditional error:
+${if;${is;${someproperty}};${error;Property someproperty must not be set}}
+Multiple error messages:
+${error;First error message;Second error message;Third error message}
+Error with computed values:
+${error;Bundle ${bsn} requires Java ${ee} but ${java.version} is configured}
+${warning} instead if you want to generate warnings without failing the buildSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/exporters.html b/org.bndtools.help/docs/macros/exporters.html new file mode 100644 index 0000000000..c7ebe60ffb --- /dev/null +++ b/org.bndtools.help/docs/macros/exporters.html @@ -0,0 +1,67 @@ + + + + +The exporters macro returns a comma-separated list of JAR names from the classpath that contain (export) a specified package. This is useful for discovering which dependencies provide a particular package.
${exporters;<package-name>}
+package-name - The fully qualified package name (e.g., "com.example.api")Find which JARs provide a specific package:
+slf4j.jars=${exporters;org.slf4j}
+# Returns: "slf4j-api-1.7.30"
+Check if a package is available:
+${if;${exporters;javax.servlet};servlet-available;servlet-missing}
+List providers of multiple packages:
+json.providers=${exporters;org.json}
+jackson.providers=${exporters;com.fasterxml.jackson.core}
+Document dependencies:
+# Package javax.xml.bind provided by: ${exporters;javax.xml.bind}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/exports.html b/org.bndtools.help/docs/macros/exports.html new file mode 100644 index 0000000000..1b9d751137 --- /dev/null +++ b/org.bndtools.help/docs/macros/exports.html @@ -0,0 +1,71 @@ + + + + +The exports macro returns a comma-separated list of packages that will be exported by the current bundle. The list can be optionally filtered by providing filter arguments.
${exports}
+${exports;<filter>...}
+filter (optional) - One or more filter patterns to select specific exported packagesGet all exported packages:
+exported.pkgs=${exports}
+# Returns: "com.example.api,com.example.impl,com.example.util"
+Document exported packages:
+Bundle-Description: Exports ${exports}
+Count exported packages:
+export.count=${size;${exports}}
+Use in conditional logic:
+${if;${exports};has-exports;no-exports}
+Filter exported packages:
+# Get only API packages (if filter parameters are supported)
+api.exports=${exports;.*\.api}
+${imports} for imported packagesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/extension.html b/org.bndtools.help/docs/macros/extension.html new file mode 100644 index 0000000000..9ac1024bc9 --- /dev/null +++ b/org.bndtools.help/docs/macros/extension.html @@ -0,0 +1,66 @@ + + + + +Extract the file extension from a file path, returning the part after the last dot in the filename.
+${extension;<path>}
+The macro:
+1. Normalizes the path (handles different path separators)
+2. Extracts the last segment (filename) from the path
+3. Returns the part after the last . in the filename
+4. Returns an empty string if no extension is present
The extension is returned without the leading dot.
+# Extract extension from file path
+${extension;myfile.txt} # Returns: txt
+${extension;/path/to/document.pdf} # Returns: pdf
+${extension;archive.tar.gz} # Returns: gz (only last extension)
+
+# No extension cases
+${extension;README} # Returns: (empty)
+${extension;/path/to/folder/} # Returns: (empty)
+
+# Multiple dots
+${extension;my.file.name.java} # Returns: java
+
+# Original examples
+${extension;abcdef.def} # Returns: def
+${extension;/foo.bar/abcdefxyz.def} # Returns: def
+${extension;abcdefxyz} # Returns: (empty)
+${extension;/foo.bar/abcdefxyz} # Returns: (empty)
+
+# Used with other macros
+-include ${extension;${workspace}/.project} == project
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/fileuri.html b/org.bndtools.help/docs/macros/fileuri.html new file mode 100644 index 0000000000..e631875fb7 --- /dev/null +++ b/org.bndtools.help/docs/macros/fileuri.html @@ -0,0 +1,79 @@ + + + + +Convert a file path to a proper file URI (e.g., file:///path/to/file).
${fileuri;<path>}
+The macro: +1. Resolves relative paths against the processor's base directory +2. Converts the path to a canonical file (resolves symbolic links, normalizes) +3. Returns the file URI representation
+The resulting URI follows the file:/// protocol format with proper escaping of special characters.
The base directory depends on context:
+- In a project's bnd.bnd: Resolved against the project directory
+- In a workspace's cnf/build.bnd: Resolved against the workspace directory
# Current directory URI
+${fileuri;.}
+# In project: file:///workspace/my.project/
+# In workspace: file:///workspace/
+
+# Relative path (resolved against base directory)
+${fileuri;src/main/resources/config.xml}
+# Returns: file:///project/base/src/main/resources/config.xml
+
+# Absolute path
+${fileuri;/home/user/file.txt}
+# Returns: file:///home/user/file.txt
+
+# Use in manifest for resource references
+Bundle-Resource: ${fileuri;${workspace}/templates/default.html}
+
+# Reference external files
+X-Config-Location: ${fileuri;../config/settings.properties}
+
+# Handle spaces and special characters (automatically escaped)
+${fileuri;/path/with spaces/file (1).txt}
+# Returns: file:///path/with%20spaces/file%20(1).txt
+. and .. removed)file:///C:/path/file.txt)See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/filter.html b/org.bndtools.help/docs/macros/filter.html new file mode 100644 index 0000000000..c0acc316a0 --- /dev/null +++ b/org.bndtools.help/docs/macros/filter.html @@ -0,0 +1,71 @@ + + + + +The filter macro filters a list to include only those entries that match a specified regular expression pattern. Entries that don't match are removed from the result.
${filter;<list>;<regex>}
+list - A comma or semicolon-separated list of valuesregex - A Java regular expression pattern to match againstFilter for JAR files:
+${filter;foo.jar,bar.txt,baz.jar;.*\.jar}
+# Returns: "foo.jar,baz.jar"
+Filter package names:
+${filter;com.example.api,com.example.impl,org.other;com\.example\..*}
+# Returns: "com.example.api,com.example.impl"
+Filter versions:
+${filter;1.0.0,2.0.0-SNAPSHOT,2.1.0;.*SNAPSHOT.*}
+# Returns: "2.0.0-SNAPSHOT"
+Filter by prefix:
+${filter;test-bundle,main-bundle,test-util;test-.*}
+# Returns: "test-bundle,test-util"
+(?i) for case-insensitive matching)${filterout} (or ${reject}) for inverse filtering${select} which is an alias for ${filter}See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/filterout.html b/org.bndtools.help/docs/macros/filterout.html new file mode 100644 index 0000000000..d890962c1f --- /dev/null +++ b/org.bndtools.help/docs/macros/filterout.html @@ -0,0 +1,72 @@ + + + + +The filterout macro filters a list to exclude entries that match a specified regular expression pattern. This is the inverse of ${filter} - entries that match the pattern are removed from the result.
${filterout;<list>;<regex>}
+list - A comma or semicolon-separated list of valuesregex - A Java regular expression pattern to match againstExclude test files:
+${filterout;Main.java,Test.java,TestUtil.java;.*Test.*}
+# Returns: "Main.java"
+Exclude snapshot versions:
+${filterout;1.0.0,2.0.0-SNAPSHOT,3.0.0;.*SNAPSHOT.*}
+# Returns: "1.0.0,3.0.0"
+Exclude specific packages:
+${filterout;com.example.api,com.example.impl,com.example.test;.*\.test}
+# Returns: "com.example.api,com.example.impl"
+Exclude by prefix:
+${filterout;prod-bundle,test-bundle,dev-bundle;test-.*|dev-.*}
+# Returns: "prod-bundle"
+(?i) for case-insensitive matching)${filter} - keeps entries that DON'T match${reject} which is an alias for ${filterout}${filter} (or ${select}) for inclusive filteringSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/find.html b/org.bndtools.help/docs/macros/find.html new file mode 100644 index 0000000000..d5fb61874e --- /dev/null +++ b/org.bndtools.help/docs/macros/find.html @@ -0,0 +1,69 @@ + + + + +The find macro returns the starting index position of a substring within a target string, or -1 if not found. This is a simple string search, not a regex pattern match.
${find;<target>;<substring>}
+target - The string to search insubstring - The substring to find (literal string, not regex)Find substring position:
+${find;hello world;world}
+# Returns: 6
+Search not found:
+${find;hello world;foo}
+# Returns: -1
+Check if string contains substring:
+${if;${matches;${find;${path};/test/};-?[0-9]+};contains-test;no-test}
+Find file extension:
+${find;${filename};.}
+${matches} for regex)${lastindexof} for last occurrence${matches} for regex matching${indexof} for list searchingSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/findfile.html b/org.bndtools.help/docs/macros/findfile.html new file mode 100644 index 0000000000..36619a2759 --- /dev/null +++ b/org.bndtools.help/docs/macros/findfile.html @@ -0,0 +1,63 @@ + + + + +layout: default +class: Project +title: findfile ';' PATH ( ';' FILTER ) +summary: Get filtered list of file paths from a directory tree
+The findfile macro recursively searches a directory and returns a filtered list of relative file paths matching the specified filter pattern.
${findfile;<directory>[;<filter>]}
+directory - Base directory to search (relative to project)filter (optional) - Instruction pattern to match files (default: all files)Find all files:
+${findfile;src}
+Find Java source files:
+${findfile;src;*.java}
+Find with exclusion pattern:
+${findfile;resources;!*.tmp}
+${findname}, ${findpath}See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/findlast.html b/org.bndtools.help/docs/macros/findlast.html new file mode 100644 index 0000000000..efb03024e5 --- /dev/null +++ b/org.bndtools.help/docs/macros/findlast.html @@ -0,0 +1,68 @@ + + + + +The findlast macro returns the index position of the last occurrence of a substring within a target string, or -1 if not found. Searches from the end backwards.
${findlast;<substring>;<target>}
+substring - The substring to find (literal string, not regex)target - The string to search inFind last occurrence:
+${findlast;/;/path/to/file.txt}
+# Returns: 8 (position of last /)
+Not found:
+${findlast;foo;hello world}
+# Returns: -1
+Extract file extension:
+${substring;${filename};${findlast;.;${filename}}}
+Get last path component:
+${substring;${path};${findlast;/;${path}}}
+${find} for first occurrence${lastindexof} for listsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/findname.html b/org.bndtools.help/docs/macros/findname.html new file mode 100644 index 0000000000..03ae3c5cc8 --- /dev/null +++ b/org.bndtools.help/docs/macros/findname.html @@ -0,0 +1,72 @@ + + + + +layout: default +class: Project +title: findname ';' REGEX ( ';' REPLACEMENT )? +summary: Find bundle resources by filename with optional regex replacement
+The findname macro finds resources in the current bundle by matching filenames (not full paths) against a regular expression, with optional replacement to transform the results.
${findname[;<regex>[;<replacement>]]}
+regex (optional) - Regular expression to match filenames (default: ".*" matches all)replacement (optional) - Replacement pattern using regex groups (e.g., "$1")Find all resources:
+${findname}
+Find by pattern:
+${findname;.*\.properties}
+# Returns filenames ending in .properties
+Find and transform:
+${findname;(.*)\.class;$1}
+# Returns class names without .class extension
+Extract parts of filenames:
+${findname;config-(.*)\.xml;$1}
+# Returns: "dev,prod,test" from "config-dev.xml", "config-prod.xml", etc.
+${findpath} for full path matching${lsr} for file system searchesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/findpath.html b/org.bndtools.help/docs/macros/findpath.html new file mode 100644 index 0000000000..8c9ad86968 --- /dev/null +++ b/org.bndtools.help/docs/macros/findpath.html @@ -0,0 +1,67 @@ + + + + +The findpath macro finds resources in the current bundle by matching full resource paths against a regular expression, with optional replacement to transform the results.
${findpath[;<regex>[;<replacement>]]}
+regex (optional) - Regular expression to match full paths (default: ".*" matches all)replacement (optional) - Replacement pattern using regex groups (e.g., "$1")Find all resources:
+${findpath}
+# Returns full paths of all resources
+Find by path pattern:
+${findpath;META-INF/.*\.xml}
+# Returns XML files (full path) in META-INF/
+Extract path components:
+${findpath;com/example/(.*)/.*\.class;$1}
+# Returns middle package component
+Find and transform:
+${findpath;src/(.*)\.java;$1}
+# Returns paths without src/ prefix and .java extension
+${findname} for filename-only matching${lsr} for file system searchesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/findproviders.html b/org.bndtools.help/docs/macros/findproviders.html new file mode 100644 index 0000000000..c64ac57851 --- /dev/null +++ b/org.bndtools.help/docs/macros/findproviders.html @@ -0,0 +1,72 @@ + + + + +The findproviders macro gives access to the resources in the Workspace repositories, including the projects itself. Its semantics match the OSGi Repository.findProviders() method.
It was added to support the use case of including a set of plugins in a resolve to create an executable. Since it is impossible to add a requirement that will add all matching resources, the findproviders macro can be used to find these resources and then turn them into initial requirements. However, this macro will likely find many other use cases since it is quite versatile. If used outside of bndrun files, it can cause circular builds because it might want to search the project that is used in. Use the REPOS Strategy in this case.
findproviders ::= namespace ( ';' FILTER ( ';' STRATEGY)? )?
+namespace ::= ... OSGi namespace
+FILTER ::= Any LDAP Styled Filter
+STRATEGY ::= 'ALL' | 'WORKSPACE' | 'REPOS'
+If no filter is given or the filter attribute is empty, then all resources that have a capability in the given namespace are returned.
+The following strategies are available:
+| Strategy | +Description | +
|---|---|
| ALL | +Searches in the Workspace and all configured Repositories | +
| REPOS | +Searches only in configured Repositories | +
| WORKSPACE | +Searches in Workspace projects only | +
The result is in PARAMETERS format, to common format in bnd and OSGi. In this case the key will be the Bundle Symbolic Name and the attributes will at least contain the Bundle version. For example:
${findproviders;osgi.service}
+This finds all resources that have an osgi.service capability. This could return something like:
com.h2database;version=1.4.198,
+org.apache.aries.async;version=1.0.1,
+org.apache.felix.configadmin;version=1.9.12,
+org.apache.felix.coordinator;version=1.0.2,
+org.apache.felix.eventadmin;version=1.5.0,
+...
+This PARAMETERS format is structured because it has attributes; that makes it harder to use with all kind of other macros. The template macro was intended to help out. This macro takes the name of macro and then treats the contents as a PARAMETERS. It then applies a template to each clause in the PARAMETERS. For example:
my.plugins = ${findproviders;osgi.service;\
+ (objectClass=com.example.my.Plugin)}
+-runrequires.plugins = ${template;my.plugins;\
+ osgi.identity;filter:='(osgi.identity=${@})'}
+This would turn the previous example into:
+-runrequires.plugins = \
+ osgi.identity;filter:='(osgi.identity=com.h2database)',
+ osgi.identity;filter:='(osgi.identity=org.apache.aries.async)',
+ ..
+The usage of the findproviders macro as depicted above also works when used in the context of one of the Maven plugins.
The first macro returns the first element from one or more comma-separated lists. If multiple lists are provided, it returns the first element from the combined lists.
${first;<list>[;<list>...]}
+list - One or more semicolon-separated lists of values (items within each list are comma-separated)Get first element from a list:
+${first;apple,banana,cherry}
+# Returns: "apple"
+Get first from multiple lists:
+${first;red,green;blue,yellow}
+# Returns: "red"
+Get first non-empty value:
+version=${first;${custom.version};${default.version};1.0.0}
+# Returns first defined version
+Use with property lists:
+primary.jar=${first;${buildpath}}
+Select first matching file:
+config.file=${first;${lsr;config;*.properties}}
+${last} for getting the last element${sublist} for extracting multiple elementsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/fmodified.html b/org.bndtools.help/docs/macros/fmodified.html new file mode 100644 index 0000000000..c310848a99 --- /dev/null +++ b/org.bndtools.help/docs/macros/fmodified.html @@ -0,0 +1,72 @@ + + + + +The fmodified macro finds the most recent modification time among a list of files and returns it as a long integer (milliseconds since epoch). This is useful for detecting when files have changed.
${fmodified;<file>[;<file>...]}
+file - One or more file paths (can be comma or semicolon-separated lists)Get modification time of a single file:
+${fmodified;build.gradle}
+# Returns: 1700000000000 (example timestamp)
+Find latest modification among multiple files:
+${fmodified;src/Main.java;src/Util.java;src/Config.java}
+# Returns timestamp of most recently modified file
+Check if source files changed:
+source.modified=${fmodified;${lsr;src;*.java}}
+Compare modification times:
+${if;${vcompare;${fmodified;input.txt};${fmodified;output.txt}};input-newer;output-newer}
+Track build inputs:
+Build-Input-Modified: ${fmodified;${buildpath}}
+${long2date} macro${if} or ${vcompare} for conditional logicSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/foreach.html b/org.bndtools.help/docs/macros/foreach.html new file mode 100644 index 0000000000..cb1ae10a22 --- /dev/null +++ b/org.bndtools.help/docs/macros/foreach.html @@ -0,0 +1,84 @@ + + + + +The foreach macro iterates over one or more lists and invokes a specified macro for each element, passing both the element value and its index (0-based) as arguments. The results are collected and returned as a comma-separated list.
${foreach;<macro-name>[;<list>...]}
+macro-name - Name of a macro to invoke for each element (without the ${} wrapper)list - One or more semicolon-separated lists to iterate over${<macro-name>;<element>;<index>}Transform each element:
+# Define a macro to wrap elements
+my-macro;$1-modified
+
+# Use foreach
+${foreach;my-macro;apple,banana,cherry}
+# Invokes: ${my-macro;apple;0}, ${my-macro;banana;1}, ${my-macro;cherry;2}
+Number items in a list:
+# Define numbering macro
+num;$2: $1
+
+${foreach;num;first,second,third}
+# Returns: "0: first,1: second,2: third"
+Process file list:
+# Define file processing macro
+process-file;processed-$1
+
+${foreach;process-file;${lsr;src;*.java}}
+Generate indexed output:
+# Define template macro
+item-template;<item index="$2">$1</item>
+
+${foreach;item-template;red,green,blue}
+# Returns XML items with indices
+${map} for simpler transformations without index${apply} for passing entire lists to a macroSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/format.html b/org.bndtools.help/docs/macros/format.html new file mode 100644 index 0000000000..fd2387dc53 --- /dev/null +++ b/org.bndtools.help/docs/macros/format.html @@ -0,0 +1,112 @@ + + + + +Format a string using printf-style format specifiers with provided arguments, similar to Java's String.format().
${format;<format-string>[;<argument>...]}
+%s, %d, %f, %tF)The macro:
+1. Processes the format string using Java's Formatter syntax
+2. Uses Locale.ROOT for build reproducibility
+3. Automatically converts string arguments to appropriate types based on format specifiers
+4. Expands arguments that look like macros (starting with ${)
+5. Supports conditional formatting with %{<condition>;<true>;<false>}
%{<condition>;<true-value>;<false-value>}
+Where <condition> is evaluated as truthy (non-empty and not "false").
# Basic string formatting
+${format;Bundle-Version %s;1.0.0}
+# Returns: Bundle-Version 1.0.0
+
+# Multiple arguments
+${format;%s-%s-%s;com;example;bundle}
+# Returns: com-example-bundle
+
+# Number formatting
+${format;Port number: %d;8080}
+# Returns: Port number: 8080
+
+# Decimal formatting
+${format;Progress: %.2f%%;75.5}
+# Returns: Progress: 75.50%
+
+# Date/time formatting
+${format;%tF;${tstamp}}
+# Returns: 2024-01-15 (ISO date format)
+
+${format;%tY-%tm-%td;${tstamp}}
+# Returns: 2024-01-15 (custom date format)
+
+# Conditional formatting
+${format;%{${debug}};DEBUG-MODE;RELEASE-MODE}
+# Returns: DEBUG-MODE if ${debug} is set, otherwise RELEASE-MODE
+
+# Width and padding
+${format;ID: %05d;42}
+# Returns: ID: 00042
+
+# Combining with other macros
+${format;Built on %tF at %tT;${now};${now}}
+# Returns: Built on 2024-01-15 at 14:30:45
+Common format specifiers include:
+%s - String%d - Decimal integer%f - Floating point%x - Hexadecimal%o - Octal%b - Boolean%tF - ISO date (yyyy-MM-dd)%tT - Time (HH:mm:ss)%tY, %tm, %td - Year, month, day componentsSee Java's Formatter documentation for complete format specifier syntax.
Locale.ROOT for consistent, reproducible formatting across environmentsString.format() and Formatter syntax${ are expanded as macros first%{...} is bnd-specific (not standard Java)See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/frange.html b/org.bndtools.help/docs/macros/frange.html new file mode 100644 index 0000000000..fab692b7bd --- /dev/null +++ b/org.bndtools.help/docs/macros/frange.html @@ -0,0 +1,76 @@ + + + + +The frange macro generates an OSGi filter expression representing a version range. By default, it creates a consumer-compatible range (major version increment). Optionally, you can request a provider-compatible range (minor version increment).
${frange;<version>[;provider]}
+${frange;<version-range>}
+version - A version string (e.g., "1.2.3") or a version range (e.g., "[1.2.3,2.0.0)")provider (optional) - Boolean (true/false) to use provider compatibility (default: false for consumer compatibility)${frange;1.2.3} → (&(version>=1.2.3)(!(version>=2.0.0)))${frange;1.2.3;true} → (&(version>=1.2.3)(!(version>=1.3.0)))${frange;[1.2.3,2.3.4)} → (&(version>=1.2.3)(!(version>=2.3.4)))Consumer compatibility range:
+Import-Package: org.example.*;version="${frange;1.2.3}"
+# Generates: (&(version>=1.2.3)(!(version>=2.0.0)))
+Provider compatibility range:
+Require-Capability: osgi.service;filter:="${frange;1.0.0;true}"
+# Generates: (&(version>=1.0.0)(!(version>=1.1.0)))
+Use with version property:
+bundle.range=${frange;${bundle.version}}
+Dependency filter:
+Require-Bundle: com.example.api;bundle-version="${frange;2.5.0}"
+Version range input:
+${frange;[1.0.0,3.0.0)}
+# Converts range to filter syntax
+version attribute${range} for generating version range notation (not filter syntax)See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/gestalt.html b/org.bndtools.help/docs/macros/gestalt.html new file mode 100644 index 0000000000..913d5e9208 --- /dev/null +++ b/org.bndtools.help/docs/macros/gestalt.html @@ -0,0 +1,71 @@ + + + + +The gestalt macro provides access to gestalt properties that describe the build environment. Gestalt is a set of parts describing the environment, where each part has a name and optional attributes. This can be configured globally or per-workspace via the -gestalt property.
${gestalt;<part>[;<key>[;<value>]]}
+part - The gestalt part namekey (optional) - Attribute key within the partvalue (optional) - Expected value to match${gestalt;<part>}): Returns the part name if it exists, empty otherwise${gestalt;<part>;<key>}): Returns the attribute value for the key, or empty if not found${gestalt;<part>;<key>;<value>}): Returns the value if it matches, empty otherwise (useful for boolean checks)Check if gestalt part exists:
+${gestalt;ci}
+# Returns: "ci" if CI gestalt is defined, "" otherwise
+Get attribute value:
+${gestalt;platform;os}
+# Returns the OS attribute value from platform gestalt
+Match specific value:
+${if;${gestalt;ci;provider;github};github-ci;other-ci}
+Configure gestalt:
+-gestalt: ci;provider=github, platform;os=linux
+Use in conditional logic:
+${if;${gestalt;ci};ci-mode;local-mode}
+-gestalt property${if} macro for environment-specific behaviorSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/get.html b/org.bndtools.help/docs/macros/get.html new file mode 100644 index 0000000000..4a68b06f25 --- /dev/null +++ b/org.bndtools.help/docs/macros/get.html @@ -0,0 +1,75 @@ + + + + +The get macro retrieves an element from one or more lists at a specified index position. It supports negative indices to count from the end of the list.
${get;<index>;<list>[;<list>...]}
+index - The zero-based position of the element to retrieve (negative values count from the end)list - One or more semicolon-separated lists (items within lists are comma-separated)Get first element (index 0):
+${get;0;apple,banana,cherry}
+# Returns: "apple"
+Get second element:
+${get;1;red,green,blue}
+# Returns: "green"
+Get last element (negative index):
+${get;-1;one,two,three}
+# Returns: "three"
+Get second-to-last element:
+${get;-2;alpha,beta,gamma,delta}
+# Returns: "gamma"
+Get from multiple lists:
+${get;3;red,green;blue,yellow}
+# Returns: "yellow" (4th element from combined list)
+${first} macro${last} macro${sublist} for extracting multiple elementsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/githead.html b/org.bndtools.help/docs/macros/githead.html new file mode 100644 index 0000000000..0b18517390 --- /dev/null +++ b/org.bndtools.help/docs/macros/githead.html @@ -0,0 +1,68 @@ + + + + +The githead macro returns the SHA-1 hash of the current Git HEAD commit. It searches for a .git directory starting from the project base directory and walking up the file hierarchy, then resolves symbolic references to find the actual commit hash.
${githead}
+None - this macro takes no parameters.
+.git directory starting from the project base, going up the directory tree.git/HEAD file to find the current commitref: refs/heads/main) to actual commit SHAsInclude Git commit in manifest:
+Bundle-SCM-Revision: ${githead}
+# Result: Bundle-SCM-Revision: 4A3B2C1D0E9F8A7B6C5D4E3F2A1B0C9D8E7F6A5B
+Create version with Git hash:
+Implementation-Version: ${version}-${githead}
+Document build source:
+Build-From: commit ${githead}
+Conditional on Git availability:
+git.revision=${if;${githead};${githead};not-in-git-repo}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/glob.html b/org.bndtools.help/docs/macros/glob.html new file mode 100644 index 0000000000..371217b020 --- /dev/null +++ b/org.bndtools.help/docs/macros/glob.html @@ -0,0 +1,78 @@ + + + + +The glob macro converts a glob pattern (shell-style wildcard pattern) into a Java regular expression. This is useful when you need regex patterns but want to write simpler glob syntax.
${glob;<glob-pattern>}
+glob-pattern - A glob pattern using wildcard syntax (e.g., *.jar, com/example/**/*.class)* → matches any characters except path separator** → matches any characters including path separators? → matches any single character[abc] → matches any character in the set{a,b,c} → matches any of the alternatives! prefixConvert simple wildcard:
+${glob;*.jar}
+# Returns regex matching JAR files
+Match with path:
+${glob;com/example/**/*.class}
+# Returns regex for any .class file under com/example/
+Use in filter:
+${filter;${packages};${glob;com.example.*}}
+# Filter packages matching glob pattern
+Negated pattern:
+${glob;!*.test.jar}
+# Returns negative lookahead regex excluding test JARs
+Multiple alternatives:
+${glob;*.{jar,war,ear}}
+# Matches files with .jar, .war, or .ear extensions
+! prefix creates a negative lookahead pattern* does not cross directory boundaries (use ** for that)See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/global.html b/org.bndtools.help/docs/macros/global.html new file mode 100644 index 0000000000..b74b031bd3 --- /dev/null +++ b/org.bndtools.help/docs/macros/global.html @@ -0,0 +1,77 @@ + + + + +The global macro provides access to user-specific settings stored in the ~/.bnd/settings.json file. This allows builds to use machine-specific configuration like credentials, API tokens, or user preferences without storing them in project files.
${global;<key>[;<default>]}
+key - The setting name to retrieve from settings.jsondefault (optional) - Default value to return if the setting is not found~/.bnd/settings.json file in their home directorykey.public - Returns the user's public key as hex stringkey.private - Returns the user's private key as hex stringAccess a custom setting:
+maven.user=${global;maven.username}
+Use with default value:
+api.key=${global;example.api.key;default-key}
+Access credentials:
+-connection: ${global;nexus.url}; \
+ uid=${global;nexus.username}; \
+ pwd=${global;nexus.password}
+Get public key:
+signing.key=${global;key.public}
+Conditional on global setting:
+${if;${global;enable.feature};enabled;disabled}
+~/.bnd/settings.jsonkey.public and key.private keys access cryptographic keysSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/ide.html b/org.bndtools.help/docs/macros/ide.html new file mode 100644 index 0000000000..dced2e9a7b --- /dev/null +++ b/org.bndtools.help/docs/macros/ide.html @@ -0,0 +1,73 @@ + + + + +The ide macro reads Java compiler source and target version settings from the Eclipse IDE's project preferences. This allows bnd builds to use the same Java version settings configured in the IDE.
${ide;<setting>[;<default>]}
+setting - The IDE setting to retrieve:javac.target - The Java target bytecode versionjavac.source - The Java source compatibility versiondefault (optional) - Default value if the setting is not found.settings/org.eclipse.jdt.core.prefs in the project directoryGet target bytecode version:
+javac.target=${ide;javac.target}
+# Returns: "1.8" (for Java 8) or "11" (for Java 11), etc.
+Get source compatibility version:
+javac.source=${ide;javac.source}
+Use with default:
+source.level=${ide;javac.source;1.8}
+target.level=${ide;javac.target;1.8}
+Configure bundle based on IDE settings:
+Bundle-RequiredExecutionEnvironment: JavaSE-${ide;javac.target}
+.settings/org.eclipse.jdt.core.prefs)javac.target and javac.source settingsTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/if.html b/org.bndtools.help/docs/macros/if.html new file mode 100644 index 0000000000..c03036c436 --- /dev/null +++ b/org.bndtools.help/docs/macros/if.html @@ -0,0 +1,28 @@ + + + + +if ::= 'if' ';' condition ';' expansion ( ';' expansion )? + condition ::= true | false + false ::= 'false' | '' + true ::= ! false + expansion ::= ...
+The ${if} macro allows a conditional expansion. The first argument is the condition. the condition is either false (empty or 'false') or otherwise true. If the condition is true, the value of the macro is the second argument, the first expansion. Otherwise, if a third argument is specified this is returned. If no third argument is specified, an empty string is returned.
Notice that if a macro is not set, it will revert to its literal value. This can be confusing. For example, if the macro foo is not set, the ${if;${foo};TRUE;FALSE} will be TRUE. If you want to handle unset as false, the def might be of use.
# expands to 'B'
+aorb = ${if;;A;B}
+
+# Display ${foo} if set to a non-empty string that is not false, otherwise 'Ouch'. See also ${def}
+whatisfoo = ${if;${foo};${foo};Ouch}
+
+# Include a file conditionally. If ${test} is not empty or false, the file is included
+-include ${if;${test};test.bnd}
+The imports macro returns a comma-separated list of packages that will be imported by the current bundle. The list can be optionally filtered by providing filter arguments.
${imports}
+${imports;<filter>...}
+filter (optional) - One or more filter patterns to select specific imported packagesGet all imported packages:
+imported.pkgs=${imports}
+# Returns: "org.osgi.framework,javax.servlet,org.slf4j,..."
+Document imported packages:
+Bundle-Description: Depends on ${imports}
+Count imported packages:
+import.count=${size;${imports}}
+Use in conditional logic:
+${if;${imports};has-imports;no-imports}
+Check for specific imports:
+has.servlet=${if;${filter;${imports};javax\.servlet.*};yes;no}
+${exports} for exported packages${exporters} to find which JARs provide packagesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/index.html b/org.bndtools.help/docs/macros/index.html new file mode 100644 index 0000000000..f925f53516 --- /dev/null +++ b/org.bndtools.help/docs/macros/index.html @@ -0,0 +1,151 @@ + + + + +The indexof macro searches for a value in combined lists and returns its 0-based index position, or -1 if not found.
${indexof;<value>;<list>[;<list>...]}
+value - The value to search forlist - One or more comma-separated lists to search inFind value in list:
+${indexof;green;red,green,blue}
+# Returns: 1
+Not found:
+${indexof;yellow;red,green,blue}
+# Returns: -1
+Search multiple lists:
+${indexof;target;${list1};${list2}}
+Check if value exists:
+${if;${matches;${indexof;test;${list}};-?[0-9]+};found;not-found}
+${lastindexof} for last occurrence${find} for substring search in stringsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/is.html b/org.bndtools.help/docs/macros/is.html new file mode 100644 index 0000000000..3e30b3d6ee --- /dev/null +++ b/org.bndtools.help/docs/macros/is.html @@ -0,0 +1,65 @@ + + + + +The is macro checks if all provided arguments are equal to each other. Returns true if all values match, false otherwise.
${is;<value>;<value>[;<value>...]}
+value - Two or more values to compare (minimum 2 required)Check equality of two values:
+${if;${is;${version};1.0.0};version-match;version-mismatch}
+Compare multiple values:
+${is;abc;abc;abc}
+# Returns: true
+Check property equality:
+${if;${is;${env};production};prod-config;dev-config}
+Validate configuration:
+${is;${driver};gradle;gradle}
+# Returns: true if driver is gradle
+${equals} for two-value comparison${if} for conditional logicSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/isdir.html b/org.bndtools.help/docs/macros/isdir.html new file mode 100644 index 0000000000..df2426405c --- /dev/null +++ b/org.bndtools.help/docs/macros/isdir.html @@ -0,0 +1,73 @@ + + + + +The isdir macro checks whether all specified file paths are directories. It returns "true" only if all paths exist and are directories; otherwise, it returns "false".
${isdir;<path>[;<path>...]}
+path - One or more file paths to check (can be absolute or relative)Check a single directory:
+${isdir;src/main/java}
+# Returns: "true" if src/main/java is a directory
+Check multiple directories:
+${isdir;src;test;resources}
+# Returns: "true" only if all three are directories
+Use in conditional logic:
+${if;${isdir;generated};directory-exists;directory-missing}
+Conditional file processing:
+${if;${isdir;${basedir}/config};${cat;config/settings.xml};default-config}
+Validate project structure:
+structure.valid=${isdir;src/main/java;src/main/resources;src/test/java}
+${isfile} for checking if paths are files${if} macro for conditional behaviorSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/isempty.html b/org.bndtools.help/docs/macros/isempty.html new file mode 100644 index 0000000000..e4b5e45e72 --- /dev/null +++ b/org.bndtools.help/docs/macros/isempty.html @@ -0,0 +1,66 @@ + + + + +The isempty macro checks if all provided arguments are empty strings. Returns true only if all values are empty, false otherwise.
${isempty[;<string>...]}
+string (optional) - Zero or more strings to checkCheck if empty:
+${if;${isempty;${value}};value-empty;value-has-content}
+Check multiple values:
+${isempty;;;;}
+# Returns: true (all empty)
+Validate required fields:
+${if;${isempty;${name};${email}};missing-fields;fields-ok}
+No arguments:
+${isempty}
+# Returns: true
+${length} to check string size${def} for default valuesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/isfile.html b/org.bndtools.help/docs/macros/isfile.html new file mode 100644 index 0000000000..b8dbf74476 --- /dev/null +++ b/org.bndtools.help/docs/macros/isfile.html @@ -0,0 +1,74 @@ + + + + +The isfile macro checks whether all specified file paths are regular files (not directories or special files). It returns "true" only if all paths exist and are regular files; otherwise, it returns "false".
${isfile;<path>[;<path>...]}
+path - One or more file paths to check (can be absolute or relative)Check a single file:
+${isfile;build.gradle}
+# Returns: "true" if build.gradle exists and is a file
+Check multiple files:
+${isfile;pom.xml;build.gradle;settings.gradle}
+# Returns: "true" only if all three are files
+Use in conditional logic:
+${if;${isfile;custom.bnd};${cat;custom.bnd};default-settings}
+Validate required files:
+${if;${isfile;LICENSE;README.md};files-present;${error;Missing required files}}
+Check file existence before processing:
+config=${if;${isfile;config.properties};${cat;config.properties};{}}
+${isdir} for checking if paths are directories${if} macro for conditional behaviorSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/isnumber.html b/org.bndtools.help/docs/macros/isnumber.html new file mode 100644 index 0000000000..73b381fa18 --- /dev/null +++ b/org.bndtools.help/docs/macros/isnumber.html @@ -0,0 +1,68 @@ + + + + +The isnumber macro checks if all provided arguments are valid numeric values. Returns true only if all arguments are numbers, false otherwise.
${isnumber;<string>[;<string>...]}
+string - One or more strings to check (minimum 1 required)Check if numeric:
+${if;${isnumber;${value}};is-numeric;not-numeric}
+Validate multiple values:
+${isnumber;123;45.6;-78}
+# Returns: true
+Not a number:
+${isnumber;abc}
+# Returns: false
+Mixed values:
+${isnumber;123;abc}
+# Returns: false (all must be numeric)
+${matches} for pattern matchingSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/join.html b/org.bndtools.help/docs/macros/join.html new file mode 100644 index 0000000000..df9423db3a --- /dev/null +++ b/org.bndtools.help/docs/macros/join.html @@ -0,0 +1,74 @@ + + + + +The join macro combines one or more lists into a single comma-separated list. It flattens all input lists and returns them joined with commas.
${join;<list>[;<list>...]}
+list - One or more lists to combine (items within lists can be comma or semicolon-separated)Join two lists:
+${join;apple,banana;cherry,date}
+# Returns: "apple,banana,cherry,date"
+Join multiple lists:
+${join;red,green;blue;yellow,orange}
+# Returns: "red,green,blue,yellow,orange"
+Flatten nested lists:
+list1=a,b,c
+list2=d,e,f
+${join;${list1};${list2}}
+# Returns: "a,b,c,d,e,f"
+Combine with other macros:
+${join;${exports};${imports}}
+# Returns all exported and imported packages
+${sjoin;<separator>;<list>...} instead${uniq} for removing duplicates${sjoin} for custom separator joiningSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/js.html b/org.bndtools.help/docs/macros/js.html new file mode 100644 index 0000000000..854596e803 --- /dev/null +++ b/org.bndtools.help/docs/macros/js.html @@ -0,0 +1,70 @@ + + + + +Deprecated: Javascript script engine removed in Java 15. This macro might not work anymore and might be removed in future versions.
+The js macro executes one or more JavaScript expressions using the Java ScriptEngine and returns the value of the last expression or any output produced.
${js;<expression>[;<expression>...]}
+expression - One or more JavaScript expressions to executedomain object (Processor instance)javascript propertySimple calculation:
+${js;2 + 2}
+# Returns: "4"
+String manipulation:
+${js;"hello".toUpperCase()}
+# Returns: "HELLO"
+Multiple expressions:
+${js;var x = 10;x * 2}
+# Returns: "20"
+Access properties:
+${js;domain.getProperty("version")}
+domain object provides access to bndSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/last.html b/org.bndtools.help/docs/macros/last.html new file mode 100644 index 0000000000..3826e75e0a --- /dev/null +++ b/org.bndtools.help/docs/macros/last.html @@ -0,0 +1,72 @@ + + + + +The last macro returns the last element from one or more comma-separated lists. If multiple lists are provided, it returns the last element from the combined lists.
${last;<list>[;<list>...]}
+list - One or more semicolon-separated lists of values (items within each list are comma-separated)Get last element from a list:
+${last;apple,banana,cherry}
+# Returns: "cherry"
+Get last from multiple lists:
+${last;red,green;blue,yellow}
+# Returns: "yellow"
+Get last defined value:
+version=${last;1.0.0;${custom.version};${snapshot.version}}
+# Returns last defined version
+Use with property lists:
+latest.jar=${last;${buildpath}}
+Select last matching file:
+newest=${last;${sort;${lsr;logs;*.log}}}
+${first} for getting the first element${get} with negative index (-1) for the same result${sublist} for extracting multiple elementsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/lastindexof.html b/org.bndtools.help/docs/macros/lastindexof.html new file mode 100644 index 0000000000..a399b1c92d --- /dev/null +++ b/org.bndtools.help/docs/macros/lastindexof.html @@ -0,0 +1,68 @@ + + + + +The lastindexof macro searches for a value in combined lists and returns the 0-based index position of its last occurrence, or -1 if not found.
${lastindexof;<value>;<list>[;<list>...]}
+value - The value to search forlist - One or more comma-separated lists to search inFind last occurrence:
+${lastindexof;blue;red,blue,green,blue}
+# Returns: 3
+Not found:
+${lastindexof;yellow;red,green,blue}
+# Returns: -1
+Search multiple lists:
+${lastindexof;target;${list1};${list2}}
+Check existence:
+${if;${matches;${lastindexof;test;${list}};-?[0-9]+};found;not-found}
+${indexof} for first occurrence${findlast} for stringsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/length.html b/org.bndtools.help/docs/macros/length.html new file mode 100644 index 0000000000..f72dd3d07d --- /dev/null +++ b/org.bndtools.help/docs/macros/length.html @@ -0,0 +1,64 @@ + + + + +The length macro returns the number of characters in a string. Returns 0 if no argument is provided.
${length[;<string>]}
+string (optional) - The string to measure. If omitted, returns 0.Get string length:
+${length;hello world}
+# Returns: 11
+Check if empty:
+${if;${length;${value}};has-value;empty}
+Validate input length:
+${if;${length;${password}};password-ok;password-required}
+No argument:
+${length}
+# Returns: 0
+${size} for list element countSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/list.html b/org.bndtools.help/docs/macros/list.html new file mode 100644 index 0000000000..b783995ecb --- /dev/null +++ b/org.bndtools.help/docs/macros/list.html @@ -0,0 +1,90 @@ + + + + +Merge multiple property values into a single comma-separated list with automatic semicolon escaping, useful for combining list-valued properties that contain semicolons.
+${list[;<property-name>...]}
+The macro:
+1. Looks up each property name to get its value
+2. Splits each value using quoted string parsing (respects quotes)
+3. Escapes unescaped semicolons within elements (replaces ; with \;)
+4. Merges all elements into a single comma-separated list
+5. Returns an empty string if no properties are specified
This is particularly useful when list elements contain semicolons (like OSGi version ranges or attributes) and need to be passed to other macros.
+# Define list properties with semicolons
+deps = com.foo;version="[1,2)", com.bar;version="[1.2,2)"
+
+# Merge and add attributes to each element
+-buildpath: ${replacelist;${list;deps};$;\\;strategy=highest}
+# Results in:
+# com.foo;version="[1,2)";strategy=highest,com.bar;version="[1.2,2)";strategy=highest
+
+# Merge multiple lists
+files = foo.jar, bar.jar
+extras = baz.jar, qux.jar
+all-files = ${list;files;extras}
+# Returns: foo.jar,bar.jar,baz.jar,qux.jar
+
+# Merge build paths with attributes
+compile-deps = javax.servlet;version=3.0, commons-io;version=2.5
+runtime-deps = slf4j-api;version=1.7
+all-deps = ${list;compile-deps;runtime-deps}
+
+# Create a combined package list with versions
+Private-Package: ${list;internal-packages;test-packages}
+
+# Handle empty properties (no-op for empty ones)
+${list;optional-deps;main-deps}
+# Only includes non-empty properties
+
+# Complex example with attributes
+base-deps = org.osgi.core;version="[6,7)", org.osgi.service.component;version="[1.3,2)"
+extra-deps = org.apache.felix.scr;version="[2,3)"
+-buildpath: ${list;base-deps;extra-deps}
+replacelist to add attributes\;See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/literal.html b/org.bndtools.help/docs/macros/literal.html new file mode 100644 index 0000000000..ce8937b821 --- /dev/null +++ b/org.bndtools.help/docs/macros/literal.html @@ -0,0 +1,70 @@ + + + + +The literal macro prevents macro expansion by wrapping a value with the macro prefix ${ and suffix }. This effectively creates a literal macro reference that won't be expanded in the current pass.
${literal;<value>}
+value - The string to wrap with macro delimiters${ and }${<value>}Create a literal macro reference:
+${literal;version}
+# Returns: "${version}"
+Delay macro expansion:
+deferred=${literal;basedir}
+# deferred contains the string "${basedir}", not the actual path
+Pass macro syntax as a value:
+template=${literal;project.name}
+# Later: expanded=${template} will evaluate ${project.name}
+Store macro patterns:
+pattern=${literal;if;condition;true;false}
+# Returns: "${if;condition;true;false}"
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/long2date.html b/org.bndtools.help/docs/macros/long2date.html new file mode 100644 index 0000000000..1a18d31c20 --- /dev/null +++ b/org.bndtools.help/docs/macros/long2date.html @@ -0,0 +1,69 @@ + + + + +The long2date macro converts a long integer representing milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC) into a human-readable date string.
${long2date;<milliseconds>}
+milliseconds - A long integer representing milliseconds since epochConvert current time:
+${long2date;${currenttime}}
+# Returns: "Wed Nov 20 10:30:45 UTC 2024" (example)
+Convert stored timestamp:
+Build-Date: ${long2date;1700000000000}
+# Returns human-readable date
+Format file modification time:
+Modified: ${long2date;${fmodified;${@}}}
+Display build timestamp:
+${long2date;${tstamp}}
+${tstamp} macro${currenttime} for getting current time in milliseconds${tstamp} for formatted timestamps${now} for seconds since epochSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/lsa.html b/org.bndtools.help/docs/macros/lsa.html new file mode 100644 index 0000000000..f5958cd679 --- /dev/null +++ b/org.bndtools.help/docs/macros/lsa.html @@ -0,0 +1,70 @@ + + + + +The lsa macro returns a comma-separated list of absolute file paths from a directory, with optional filtering using selectors.
${lsa;<directory>[;<selector>...]}
+directory - Directory path to list (relative to project base)selector (optional) - One or more file selectors/patterns to filter resultsList all files:
+${lsa;src/main/java}
+# Returns absolute paths
+Filter by pattern:
+${lsa;lib;*.jar}
+# Lists all JAR files with absolute paths
+Multiple filters:
+${lsa;resources;*.xml;*.properties}
+Use in manifest:
+Resources: ${lsa;config}
+${lsr} for relative paths${glob} for pattern matching${findfile} for recursive searchTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/lsr.html b/org.bndtools.help/docs/macros/lsr.html new file mode 100644 index 0000000000..cc3839076b --- /dev/null +++ b/org.bndtools.help/docs/macros/lsr.html @@ -0,0 +1,71 @@ + + + + +The lsr macro returns a comma-separated list of relative file paths from a directory, with optional filtering using selectors.
${lsr;<directory>[;<selector>...]}
+directory - Directory path to list (relative to project base)selector (optional) - One or more file selectors/patterns to filter resultsList all files:
+${lsr;src/main/java}
+# Returns relative paths
+Filter by pattern:
+${lsr;lib;*.jar}
+# Lists all JAR files with relative paths
+Find source files:
+${lsr;src;*.java}
+# All Java source files
+Multiple patterns:
+${lsr;resources;*.xml;*.properties}
+${lsa} for absolute paths${glob} for pattern matching${findfile} for filtered recursive searchTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/map.html b/org.bndtools.help/docs/macros/map.html new file mode 100644 index 0000000000..3dbd136d48 --- /dev/null +++ b/org.bndtools.help/docs/macros/map.html @@ -0,0 +1,83 @@ + + + + +The map macro applies a transformation function (specified as a macro name) to each element of one or more lists, collecting the results into a new comma-separated list. This is similar to the functional programming map operation.
${map;<macro-name>[;<list>...]}
+macro-name - Name of a macro to invoke for each element (without the ${} wrapper)list - One or more semicolon-separated lists to process${<macro-name>;<element>}Transform list elements:
+# Define transformation macro
+upper;${toupper;$1}
+
+# Map to uppercase
+${map;upper;apple,banana,cherry}
+Add prefix to each element:
+# Define prefix macro
+add-prefix;modified-$1
+
+${map;add-prefix;file1,file2,file3}
+# Returns: "modified-file1,modified-file2,modified-file3"
+Process file list:
+# Define file processor
+process;${basename;$1}
+
+${map;process;/path/to/file1.jar;/path/to/file2.jar}
+# Returns basenames of files
+Calculate transformations:
+# Define calculation macro
+double;${multiply;$1;2}
+
+${map;double;1,2,3,4,5}
+# Returns: "2,4,6,8,10"
+${foreach}, no index is passed to the macro${foreach} for transformations that need the element index${apply} for passing entire lists to a macroSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/matches.html b/org.bndtools.help/docs/macros/matches.html new file mode 100644 index 0000000000..d211443b1d --- /dev/null +++ b/org.bndtools.help/docs/macros/matches.html @@ -0,0 +1,66 @@ + + + + +The matches macro tests whether a string matches a given regular expression pattern. Returns true if the entire string matches, false otherwise.
${matches;<string>;<regex>}
+string - The string to testregex - The regular expression pattern (Java regex syntax)Check pattern match:
+${if;${matches;v1.2.3;v[0-9]+\.[0-9]+\.[0-9]+};valid-version;invalid}
+Validate format:
+${matches;com.example.api;com\.example\..*}
+# Returns: true
+Check numeric:
+${if;${matches;${value};[0-9]+};is-number;not-number}
+Test package pattern:
+${matches;${package};com\..*\.impl}
+(?i) in pattern${filter} for list filtering${find} for substring searchSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/maven_version.html b/org.bndtools.help/docs/macros/maven_version.html new file mode 100644 index 0000000000..22f94056d6 --- /dev/null +++ b/org.bndtools.help/docs/macros/maven_version.html @@ -0,0 +1,52 @@ + + + + +Convert a Maven-style version string to OSGi version format. Note: Use version_cleanup instead, which can be used in more contexts.
+${maven_version;<version>}
+This macro is deprecated in favor of version_cleanup.
+Converts Maven version formats (which may use hyphens) to OSGi version format (which uses dots):
+- Replaces hyphens with dots in the qualifier
+- Ensures the version follows OSGi's major.minor.micro.qualifier structure
For example: 1.2.3-SNAPSHOT becomes 1.2.3.SNAPSHOT
# Convert Maven version
+${maven_version;1.2.3-SNAPSHOT}
+# Returns: 1.2.3.SNAPSHOT
+
+# In Bundle-Version
+Bundle-Version: ${maven_version;${project.version}}
+
+# However, prefer version_cleanup:
+Bundle-Version: ${version_cleanup;${project.version}}
+This macro should not be used in new code. Use version_cleanup instead because:
+- version_cleanup can be used in more contexts (not just Builder)
+- version_cleanup handles more edge cases and version formats
+- version_cleanup is the recommended approach going forward
See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/max.html b/org.bndtools.help/docs/macros/max.html new file mode 100644 index 0000000000..fed42d2fec --- /dev/null +++ b/org.bndtools.help/docs/macros/max.html @@ -0,0 +1,64 @@ + + + + +The max macro compares strings lexicographically using Java's String.compareTo() and returns the maximum (highest) value.
${max;<list>[;<list>...]}
+list - One or more semicolon-separated lists of stringsFind maximum string:
+${max;apple,banana,cherry}
+# Returns: "cherry"
+Multiple lists:
+${max;abc,xyz;def,mno}
+# Returns: "xyz"
+Numeric strings (lexicographic):
+${max;1,10,2,20}
+# Returns: "20" (lexicographic, not numeric)
+${min} for minimum${vmax} for version comparison${nmax} for numeric comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/md5.html b/org.bndtools.help/docs/macros/md5.html new file mode 100644 index 0000000000..a9067b46e5 --- /dev/null +++ b/org.bndtools.help/docs/macros/md5.html @@ -0,0 +1,69 @@ + + + + +The md5 macro calculates the MD5 hash of a resource that exists within the current bundle JAR. The result can be returned as either Base64 (default) or hexadecimal encoding.
${md5;<resource-path>[;<encoding>]}
+resource-path - Path to a resource within the bundle (e.g., "META-INF/MANIFEST.MF")encoding (optional) - Output encoding: "hex" for hexadecimal, "base64" for Base64 (default)Get MD5 of a resource (Base64):
+${md5;META-INF/services/com.example.Service}
+# Returns Base64-encoded MD5
+Get MD5 in hexadecimal format:
+${md5;config/application.properties;hex}
+# Returns: "5d41402abc4b2a76b9719d911017c592" (example)
+Hash manifest file:
+manifest.md5=${md5;META-INF/MANIFEST.MF;hex}
+Verify resource integrity:
+Bundle-ResourceMD5: ${md5;important-config.xml}
+${sha1} and ${digest} for other hash algorithmsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/min.html b/org.bndtools.help/docs/macros/min.html new file mode 100644 index 0000000000..f0b31e8fbb --- /dev/null +++ b/org.bndtools.help/docs/macros/min.html @@ -0,0 +1,64 @@ + + + + +The min macro compares strings lexicographically using Java's String.compareTo() and returns the minimum (lowest) value.
${min;<list>[;<list>...]}
+list - One or more semicolon-separated lists of stringsFind minimum string:
+${min;apple,banana,cherry}
+# Returns: "apple"
+Multiple lists:
+${min;xyz,abc;mno,def}
+# Returns: "abc"
+Numeric strings (lexicographic):
+${min;1,10,2,20}
+# Returns: "1" (lexicographic, not numeric)
+${max} for maximum${vmin} for version comparison${nmin} for numeric comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/native_capability.html b/org.bndtools.help/docs/macros/native_capability.html new file mode 100644 index 0000000000..9b87ba4d78 --- /dev/null +++ b/org.bndtools.help/docs/macros/native_capability.html @@ -0,0 +1,78 @@ + + + + +The native_capability macro generates an OSGi native capability string in the format specified for Provide-Capability or Require-Capability headers. It represents the native platform according to OSGi RFC 188, automatically detecting the current platform or using explicit overrides.
${native_capability[;<property>=<value>...]}
+os.name=<value> (optional) - Override OS name (e.g., "Linux", "Windows 7")os.version=<value> (optional) - Override OS version (e.g., "3.2.4", "6.1.0")os.processor=<value> (optional) - Override processor (e.g., "x86-64", "aarch64")Auto-detect current platform:
+${native_capability}
+# On Windows 7 x64:
+# osgi.native;osgi.native.osname:List<String>="Windows7,Windows 7,Win32";
+# osgi.native.osversion:Version=6.1.0;
+# osgi.native.processor:List<String>="x86-64,amd64,em64t,x86_64"
+Override OS name:
+${native_capability;os.name=Linux}
+Specify complete platform:
+${native_capability;os.name=Linux;os.version=3.2.4;os.processor=x86-64}
+Use in capability header:
+Provide-Capability: ${native_capability}
+Cross-platform requirement:
+Require-Capability: ${native_capability;os.name=MacOSX;os.version=10.15.0}
+TODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/ncompare.html b/org.bndtools.help/docs/macros/ncompare.html new file mode 100644 index 0000000000..3c326ac1b9 --- /dev/null +++ b/org.bndtools.help/docs/macros/ncompare.html @@ -0,0 +1,92 @@ + + + + +Compare two numbers numerically using Java's Double.compare() method, returning -1, 0, or 1.
${ncompare;<number1>;<number2>}
+Compares the two numbers numerically and returns:
+- 0 - The numbers are equal
+- 1 - The first number is greater than the second
+- -1 - The first number is less than the second
Numbers are parsed as doubles, supporting both integers and floating-point values.
+# Equal numbers
+${ncompare;42;42}
+# Returns: 0
+
+# First number greater
+${ncompare;100;50}
+# Returns: 1
+
+# First number less
+${ncompare;10;20}
+# Returns: -1
+
+# Floating-point comparison
+${ncompare;3.14;2.71}
+# Returns: 1
+
+# Negative numbers
+${ncompare;-5;-10}
+# Returns: 1 (-5 is greater than -10)
+
+# Use in conditional logic
+-include ${if;${ncompare;${Bundle-Version};2.0};modern.bnd;legacy.bnd}
+
+# Compare with zero
+${ncompare;${errorcount};0}
+# Returns: 1 if errors exist, 0 if no errors
+
+# Numeric comparison (not lexicographic)
+${ncompare;10;2}
+# Returns: 1 (10 is greater than 2 numerically)
+
+# Compare with macro values
+${ncompare;${size;${errors}};5}
+# Check if more than 5 errors
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/nmax.html b/org.bndtools.help/docs/macros/nmax.html new file mode 100644 index 0000000000..a047268ae8 --- /dev/null +++ b/org.bndtools.help/docs/macros/nmax.html @@ -0,0 +1,63 @@ + + + + +The nmax macro compares numeric values using floating-point comparison and returns the maximum (highest) number.
${nmax;<list>[;<list>...]}
+list - One or more semicolon-separated lists of numeric valuesFind maximum number:
+${nmax;10,5,20,3}
+# Returns: "20"
+Multiple lists:
+${nmax;100,200;50,150}
+# Returns: "200"
+Decimals:
+${nmax;1.5,2.3,0.9}
+# Returns: "2.3"
+${nmin} for minimum${max} for string comparison${vmax} for version comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/nmin.html b/org.bndtools.help/docs/macros/nmin.html new file mode 100644 index 0000000000..47b39ceee7 --- /dev/null +++ b/org.bndtools.help/docs/macros/nmin.html @@ -0,0 +1,63 @@ + + + + +The nmin macro compares numeric values using floating-point comparison and returns the minimum (lowest) number.
${nmin;<list>[;<list>...]}
+list - One or more semicolon-separated lists of numeric valuesFind minimum number:
+${nmin;10,5,20,3}
+# Returns: "3"
+Multiple lists:
+${nmin;100,200;50,150}
+# Returns: "50"
+Decimals:
+${nmin;1.5,2.3,0.9}
+# Returns: "0.9"
+${nmax} for maximum${min} for string comparison${vmin} for version comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/now.html b/org.bndtools.help/docs/macros/now.html new file mode 100644 index 0000000000..fc0b52a6a7 --- /dev/null +++ b/org.bndtools.help/docs/macros/now.html @@ -0,0 +1,71 @@ + + + + +The now macro returns the current date and time. It can return the raw Date object, milliseconds since epoch, or a formatted date string.
${now[;<format>]}
+format (optional) - Either "long" for milliseconds, a date format pattern, or omit for default Date formatGet current time in millis:
+${now;long}
+# Returns: 1700000000000 (example)
+Custom format:
+${now;yyyy-MM-dd HH:mm:ss}
+# Returns: "2024-11-20 10:30:45"
+ISO format:
+${now;yyyy-MM-dd'T'HH:mm:ss'Z'}
+# Returns: "2024-11-20T10:30:45Z"
+Default format:
+${now}
+# Returns: "Wed Nov 20 10:30:45 UTC 2024"
+Build timestamp:
+Build-Time: ${now;yyyy-MM-dd HH:mm z}
+${tstamp} for more control${currenttime} for millis onlySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/nsort.html b/org.bndtools.help/docs/macros/nsort.html new file mode 100644 index 0000000000..8005b1f69f --- /dev/null +++ b/org.bndtools.help/docs/macros/nsort.html @@ -0,0 +1,71 @@ + + + + +The nsort macro combines one or more lists and sorts their contents numerically. Unlike alphabetic sorting, it treats values as numbers, so "2" comes before "10".
${nsort;<list>[;<list>...]}
+list - One or more semicolon-separated lists to sort (items within lists are comma-separated)Sort numbers correctly:
+${nsort;10,2,1,20,3}
+# Returns: "1,2,3,10,20" (not "1,10,2,20,3")
+Sort with leading zeros:
+${nsort;001,010,002,100}
+# Returns: "1,2,10,100" (leading zeros removed)
+Combine and sort multiple lists:
+${nsort;5,15,25;10,20,30}
+# Returns: "5,10,15,20,25,30"
+Sort version-like numbers:
+${nsort;1,11,2,21,3}
+# Returns: "1,2,3,11,21"
+${sort} for alphabetic sorting${vcompare} for semantic version comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/osfile.html b/org.bndtools.help/docs/macros/osfile.html new file mode 100644 index 0000000000..0216e199ab --- /dev/null +++ b/org.bndtools.help/docs/macros/osfile.html @@ -0,0 +1,69 @@ + + + + +The osfile macro constructs an absolute file path by combining a base directory and a relative path, using the operating system's native path separator (e.g., backslash on Windows, forward slash on Unix).
${osfile;<base>;<path>}
+base - The base directory pathpath - The relative path to append to the baseCreate OS-specific path on Unix:
+${osfile;/home/user;project/src/Main.java}
+# Returns: "/home/user/project/src/Main.java"
+Create OS-specific path on Windows:
+${osfile;C:/Users/user;project\\src\\Main.java}
+# Returns: "C:\Users\user\project\src\Main.java"
+Build path from project base:
+config.file=${osfile;${basedir};config/app.properties}
+Create platform-specific classpath entry:
+jar.path=${osfile;${basedir};lib/dependency.jar}
+${path} for Unix-style paths${pathseparator} for the OS path separator${basedir} for getting the project base directorySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_allsourcepath.html b/org.bndtools.help/docs/macros/p_allsourcepath.html new file mode 100644 index 0000000000..2956d35395 --- /dev/null +++ b/org.bndtools.help/docs/macros/p_allsourcepath.html @@ -0,0 +1,53 @@ + + + + +layout: default +class: Project +title: p_allsourcepath +summary: Get paths to all source directories
+The p_allsourcepath macro returns all source directories for the project, including those from dependencies, as a comma-separated list.
${p_allsourcepath}
+None - this macro takes no parameters.
+Get all source paths:
+All-Sources: ${p_allsourcepath}
+${p_sourcepath}${p_sourcepath} for project sources onlySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_bootclasspath.html b/org.bndtools.help/docs/macros/p_bootclasspath.html new file mode 100644 index 0000000000..d338ad3557 --- /dev/null +++ b/org.bndtools.help/docs/macros/p_bootclasspath.html @@ -0,0 +1,53 @@ + + + + +layout: default +class: Project +title: p_bootclasspath +summary: Get the project's boot classpath
+The p_bootclasspath macro returns the project's boot classpath (Java runtime libraries) as a comma-separated list of paths.
${p_bootclasspath}
+None - this macro takes no parameters.
+Get boot classpath:
+Boot-Classpath: ${p_bootclasspath}
+rt.jar or modules${p_buildpath} for project dependenciesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_buildpath.html b/org.bndtools.help/docs/macros/p_buildpath.html new file mode 100644 index 0000000000..58be720e1c --- /dev/null +++ b/org.bndtools.help/docs/macros/p_buildpath.html @@ -0,0 +1,58 @@ + + + + +layout: default +class: Project +title: p_buildpath +summary: Get the project's buildpath as a list
+The p_buildpath macro returns the project's buildpath (compile-time dependencies) as a comma-separated list of file paths.
${p_buildpath}
+None - this macro takes no parameters.
+-buildpath entriesGet buildpath:
+Build-Dependencies: ${p_buildpath}
+Count buildpath entries:
+dependency.count=${size;${split;,;${p_buildpath}}}
+-buildpath entries${p_testpath} for test dependencies${p_dependson} for project dependenciesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_dependson.html b/org.bndtools.help/docs/macros/p_dependson.html new file mode 100644 index 0000000000..7003664408 --- /dev/null +++ b/org.bndtools.help/docs/macros/p_dependson.html @@ -0,0 +1,52 @@ + + + + +layout: default +class: Project +title: p_dependson +summary: Get list of project names this project depends on
+The p_dependson macro returns a comma-separated list of project names that the current project depends on (as specified in the -dependson directive).
${p_dependson}
+None - this macro takes no parameters.
+-dependson directiveGet dependent projects:
+Depends-On: ${p_dependson}
+-dependson directive${p_buildpath} for JAR dependenciesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_output.html b/org.bndtools.help/docs/macros/p_output.html new file mode 100644 index 0000000000..ac75295920 --- /dev/null +++ b/org.bndtools.help/docs/macros/p_output.html @@ -0,0 +1,57 @@ + + + + +layout: default +class: Project +title: p_output +summary: Get the absolute path to the project's output directory
+The p_output macro returns the absolute path to the project's output/target directory where compiled classes and built artifacts are placed.
${p_output}
+None - this macro takes no parameters.
+bin or target directoryGet output directory:
+Output-Directory: ${p_output}
+Reference output in paths:
+classes.location=${p_output}/classes
+bin or target directory${p_sourcepath} for source directories${basedir} for project rootSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_sourcepath.html b/org.bndtools.help/docs/macros/p_sourcepath.html new file mode 100644 index 0000000000..6a251b572b --- /dev/null +++ b/org.bndtools.help/docs/macros/p_sourcepath.html @@ -0,0 +1,53 @@ + + + + +layout: default +class: Project +title: p_sourcepath +summary: Get the project's source directories
+The p_sourcepath macro returns the project's source directories as a comma-separated list of paths.
${p_sourcepath}
+None - this macro takes no parameters.
+src directoryGet source paths:
+Source-Directories: ${p_sourcepath}
+src directory${p_buildpath} for dependencies${p_output} for output directorySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/p_testpath.html b/org.bndtools.help/docs/macros/p_testpath.html new file mode 100644 index 0000000000..6b1a8f969c --- /dev/null +++ b/org.bndtools.help/docs/macros/p_testpath.html @@ -0,0 +1,53 @@ + + + + +layout: default +class: Project +title: p_testpath +summary: Get the project's test runtime path
+The p_testpath macro returns the test runtime path (JARs placed on the classpath for testing) as a comma-separated list of file paths.
${p_testpath}
+None - this macro takes no parameters.
+-runpath entries for testingGet test path:
+Test-Dependencies: ${p_testpath}
+-runpath entries${p_buildpath} for compile dependenciesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/packageattribute.html b/org.bndtools.help/docs/macros/packageattribute.html new file mode 100644 index 0000000000..6dac7c2e80 --- /dev/null +++ b/org.bndtools.help/docs/macros/packageattribute.html @@ -0,0 +1,14 @@ + + + + +The packageattribute macro returns the value of the specified attribute name for the
+specified package name. These values are only available after analysis is complete. The
+default attribute to return is the version.
The packages macro provides a query function over the contained packages of a bundle. A simple query language is used to query the packages and filter them.
+For example if you want to export all packages that are annotated with the @org.example.Export annotation:
-exportcontents: ${packages;ANNOTATED;org.example.Export}
+(NB. using the macro inside Export-Package/Private-Package is an error, because they define the content of the bundle. The packages macro can only be used in the final manifest calculation.).
All pattern matching is based on fully qualified name and uses the globbing model.
+More examples:
+# List all packages in the bundle, irrespective of how they were included
+All-Packages: ${packages}
+
+# List all packages, alternative syntax
+All-Packages: ${packages;ANY}
+
+# Export packages containing the substring 'export'
+-exportcontents: ${packages;NAMED;*export*}
+
+# Export packages that contain a version. Useful when wrapping existing bundles while keeping exports intact
+-exportcontents: ${packages;VERSIONED}
+
+# List of packages that were included in the bundle as conditional packages
+Added: ${packages;CONDITIONAL}
+The following table specifies the available query options:
+| Query | +Parameter | +Description | +
|---|---|---|
| ANY | ++ | Matches any package | +
| ANNOTATED | +PATTERN | +The package must have an annotation that matches the pattern. The annotation must be either CLASS or RUNTIME retained, and placed on the `package-info.class` for the package. | +
| NAMED | +PATTERN | +The package FQN must match the given pattern. | +
| VERSIONED | ++ | Packages that are versioned. Usually this means exported packages. | +
| CONDITIONAL | ++ | Packages that were included in the bundle as conditional packages. That is, + by using -conditionalpackage or + Conditional-Package. | +
The path macro joins one or more lists of file paths using the operating system's path separator (: on Unix, ; on Windows). This is useful for creating classpaths and other path lists.
${path;<list>[;<list>...]}
+list - One or more semicolon-separated lists of file paths: (colon); (semicolon)Create a classpath on Unix:
+${path;lib/a.jar,lib/b.jar,lib/c.jar}
+# Returns: "lib/a.jar:lib/b.jar:lib/c.jar"
+Create a classpath on Windows:
+${path;lib/a.jar,lib/b.jar,lib/c.jar}
+# Returns: "lib/a.jar;lib/b.jar;lib/c.jar"
+Combine multiple path lists:
+${path;${buildpath};${testpath}}
+# Joins both lists with OS separator
+Build Java classpath:
+-classpath=${path;${lsr;lib;*.jar}}
+:, Windows uses semicolon ;${pathseparator} to get the separator itself${osfile} for OS-specific file paths${join} for comma-separated joiningSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/pathseparator.html b/org.bndtools.help/docs/macros/pathseparator.html new file mode 100644 index 0000000000..5d3a320f3f --- /dev/null +++ b/org.bndtools.help/docs/macros/pathseparator.html @@ -0,0 +1,63 @@ + + + + +The pathseparator macro returns the operating system's path separator character used to separate entries in path lists like classpaths.
${pathseparator}
+None - this macro takes no parameters.
+: (colon) on Unix/Linux/Mac systems; (semicolon) on Windows systemsFile.pathSeparator in JavaGet the path separator:
+${pathseparator}
+# Returns: ":" on Unix, ";" on Windows
+Build a path manually:
+classpath=lib/a.jar${pathseparator}lib/b.jar${pathseparator}lib/c.jar
+Use in conditionals:
+${if;${equals;${pathseparator};:};unix;windows}
+Split paths:
+${split;${pathseparator};${some.path}}
+: or ;/ or \)${separator} for the file separator${path} which automatically uses the correct separatorSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/permissions.html b/org.bndtools.help/docs/macros/permissions.html new file mode 100644 index 0000000000..dfcc623e62 --- /dev/null +++ b/org.bndtools.help/docs/macros/permissions.html @@ -0,0 +1,77 @@ + + + + +The permissions macro generates OSGi permission declarations in the format required for the OSGi permissions resource (OSGI-INF/permissions.perm). It can generate package permissions and admin permissions.
${permissions[;<type>...]}
+type (optional) - One or more permission types:packages - Generate PackagePermission for imports and exportsadmin - Generate AdminPermissionall - Generate all permission typespermissions - No-op markerGenerate package permissions:
+${permissions;packages}
+# Returns:
+# (org.osgi.framework.PackagePermission "com.example.api" "import")
+# (org.osgi.framework.PackagePermission "com.example.impl" "export")
+Generate admin permissions:
+${permissions;admin}
+# Returns: (org.osgi.framework.AdminPermission)
+Generate all permissions:
+${permissions;all}
+Use in permissions file:
+# In OSGI-INF/permissions.perm
+${permissions;packages}
+${permissions;admin}
+TODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/propertiesdir.html b/org.bndtools.help/docs/macros/propertiesdir.html new file mode 100644 index 0000000000..50d86b92db --- /dev/null +++ b/org.bndtools.help/docs/macros/propertiesdir.html @@ -0,0 +1,65 @@ + + + + +layout: default +title: propertiesdir +class: Processor +summary: Get the directory containing the current properties file
+The propertiesdir macro returns the absolute path to the directory containing the current properties file being processed.
${propertiesdir}
+None - this macro takes no parameters and will error if arguments are provided.
+Get properties directory:
+Config-Directory: ${propertiesdir}
+Reference sibling files:
+license.file=${propertiesdir}/LICENSE.txt
+Relative resources:
+resources=${propertiesdir}/resources
+${propertiesname} for filename${thisfile} for full path${basedir} for project rootTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/propertiesname.html b/org.bndtools.help/docs/macros/propertiesname.html new file mode 100644 index 0000000000..1bfdd77922 --- /dev/null +++ b/org.bndtools.help/docs/macros/propertiesname.html @@ -0,0 +1,65 @@ + + + + +layout: default +title: propertiesname +class: Project +summary: Get the filename of the current properties file
+The propertiesname macro returns the filename (without path) of the properties file being processed. This is typically bnd.bnd for projects or workspace configuration files.
${propertiesname}
+None - this macro takes no parameters.
+Get current file name:
+Current-File: ${propertiesname}
+# Returns: "bnd.bnd" or "my.bndrun", etc.
+Use in logging:
+Build-Info: Built from ${propertiesname}
+Conditional based on file:
+${if;${equals;${propertiesname};test.bnd};test-mode;normal-mode}
+${propertiesdir} for the directory path${basedir} for the project base directoryTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/rand.html b/org.bndtools.help/docs/macros/rand.html new file mode 100644 index 0000000000..f931cd2079 --- /dev/null +++ b/org.bndtools.help/docs/macros/rand.html @@ -0,0 +1,68 @@ + + + + +The rand macro generates a random integer. By default it returns a number between 0 and 100, or within a specified min/max range (inclusive).
${rand[;<max>[;<min>]]}
+max (optional) - Maximum value (default: 100)min (optional) - Minimum value (default: 0)Default range (0-100):
+${rand}
+# Returns: 42 (example)
+Custom max:
+${rand;10}
+# Returns: 7 (between 0-10)
+Custom range:
+${rand;100;50}
+# Returns: 73 (between 50-100)
+Generate unique build number:
+build.number=${rand;9999;1000}
+${random} for random stringsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/random.html b/org.bndtools.help/docs/macros/random.html new file mode 100644 index 0000000000..2601ba0ac4 --- /dev/null +++ b/org.bndtools.help/docs/macros/random.html @@ -0,0 +1,72 @@ + + + + +The random macro generates a random string that is guaranteed to be a valid Java identifier. The first character is always a letter, and subsequent characters are letters or numbers.
${random[;<length>]}
+length (optional) - Number of characters to generate (default: 8)Generate default 8-character identifier:
+${random}
+# Returns: "aB3xY9kL" (example)
+Generate specific length:
+${random;12}
+# Returns: "K5mPqR7sT2uV" (example, 12 chars)
+Create unique package name:
+test.package=${random;16}
+Generate unique property:
+temp.id.${random;6}=value
+Create unique class names:
+TestClass${random;4}
+${rand} for random numbersSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/range.html b/org.bndtools.help/docs/macros/range.html new file mode 100644 index 0000000000..4aae91f470 --- /dev/null +++ b/org.bndtools.help/docs/macros/range.html @@ -0,0 +1,53 @@ + + + + +The range macro takes a version range mask/template and uses it calculate a range from a given version. The primary reason for the ${range} macro is to be used in the version policy. With the version policy we have a version of an exported package and we need to calculate the range for that. The rules come from the consumer or provider policy. However, this policy can be overridden on the Import-Package header by specifying the version as a range macro:
Import-Package: com.example.myownpolicy; version="${range;[==,=+)}", *
+Since the version for the exported package is set as ${@}, the macro will calculate the proper semantic range for a provider.
The syntax for the range macro is:
range ::= ( '\[' |'\( ) mask ',' mask ( '\)' | '\]' )
+mask ::= m ( m ( m )? )? q
+m ::= [0-9=+-~]
+q ::= [0-9=~Ss]
+The meaning of the characters is:
+= – Keep the version part- – Decrement the version part+ – Increment the version part[0-9] – Replace the version part~ – Ignore the version part[Ss] – If the qualifier equals SNAPSHOT, then it will return a maven like snapshot version. Maven snapshot versions do not use the . as the separator but a - sign. The upper case S checks case sensitive, the lower case s is case insensitive. This template character will be treated as the last character in the template and return the version immediately. With ${range}:
${range;[==,+);1.2.3} will return [1.2,2).${range;[===,+++);1.2.3} will return [1.2.3,2.3.4).${range;[===,+==);1.2.3} will return [1.2.3,2.2.3).${range;[===,==+);1.2.3} will return [1.2.3,1.2.4).${range;[=+=,+=+);1.2.3} will return [1.3.3,2.2.4).With ${versionmask}:
${versionmask;===S;1.2.3.SNAPSHOT} will return 1.2.3-SNAPSHOT.[${versionmask;==;1.2.3},${versionmask;+;1.2.3}) will return [1.2,2).[${versionmask;===;1.2.3},${versionmask;+++;1.2.3}) will return [1.2.3,2.3.4).[${versionmask;===;1.2.3},${versionmask;+==;1.2.3}) will return [1.2.3,2.2.3).[${versionmask;===;1.2.3},${versionmask;==+;1.2.3}) will return [1.2.3,1.2.4).[${versionmask;=+=;1.2.3},${versionmask;+=+;1.2.3}) will return [1.3.3,2.2.4).Also see versionmask / version.
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/reject.html b/org.bndtools.help/docs/macros/reject.html new file mode 100644 index 0000000000..ba87efaa77 --- /dev/null +++ b/org.bndtools.help/docs/macros/reject.html @@ -0,0 +1,72 @@ + + + + +Filter a list to exclude elements that match a regular expression. This macro is an alias for the filterout macro.
+${reject;<list>;<regex>}
+Returns a new list containing only the elements from the input list that do not match the specified regular expression pattern.
+This macro is functionally identical to filterout.
+# Reject items starting with "test"
+packages = com.example.api, com.example.test, com.example.impl
+${reject;${packages};.*\.test}
+# Returns: com.example.api,com.example.impl
+
+# Exclude test files
+files = Main.java, Test.java, Helper.java, TestHelper.java
+${reject;${files};.*Test.*}
+# Returns: Main.java,Helper.java
+
+# Exclude internal packages
+all-packages = com.company.api, com.company.internal, com.company.util
+-exportpackage: ${reject;${all-packages};.*\.internal.*}
+# Returns: com.company.api,com.company.util
+
+# Remove snapshot versions
+versions = 1.0.0, 2.0.0-SNAPSHOT, 3.0.0
+${reject;${versions};.*SNAPSHOT.*}
+# Returns: 1.0.0,3.0.0
+.* for partial matching)TODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/removeall.html b/org.bndtools.help/docs/macros/removeall.html new file mode 100644 index 0000000000..dfe8051ebc --- /dev/null +++ b/org.bndtools.help/docs/macros/removeall.html @@ -0,0 +1,82 @@ + + + + +Remove all elements from the first list that are present in the second list, returning the filtered result.
+${removeall;<list1>[;<list2>]}
+# Remove specific packages
+all-packages = com.example.api, com.example.impl, com.example.test
+test-packages = com.example.test
+production-packages = ${removeall;${all-packages};${test-packages}}
+# Returns: com.example.api,com.example.impl
+
+# Remove dependencies
+all-deps = foo.jar, bar.jar, baz.jar, test.jar
+test-deps = test.jar
+runtime-deps = ${removeall;${all-deps};${test-deps}}
+# Returns: foo.jar,bar.jar,baz.jar
+
+# Remove multiple items
+items = a, b, c, d, e
+to-remove = b, d
+result = ${removeall;${items};${to-remove}}
+# Returns: a,c,e
+
+# No second list - returns empty
+${removeall;foo,bar,baz}
+# Returns: (empty)
+
+# Remove from build path
+full-buildpath = ${buildpath}, ${testpath}
+exclude-items = junit, mockito
+-buildpath: ${removeall;${full-buildpath};${exclude-items}}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/replace.html b/org.bndtools.help/docs/macros/replace.html new file mode 100644 index 0000000000..0d727511d0 --- /dev/null +++ b/org.bndtools.help/docs/macros/replace.html @@ -0,0 +1,73 @@ + + + + +The replace macro applies regex-based replacement to each element in a comma-separated list. Uses simple comma splitting (doesn't handle quoted sections).
${replace;<list>;<regex>[;<replacement>[;<delimiter>]]}
+list - Comma-separated list of elementsregex - Regular expression pattern to matchreplacement (optional) - Replacement string with $1-$9 back-references (default: empty)delimiter (optional) - Output delimiter (default: ",")\s*,\s*)element.replaceAll(regex, replacement) to each$1, $2, etc.)Add file extension:
+impls: foo,bar
+${replace;${impls};$;.jar}
+# Returns: "foo.jar,bar.jar"
+Replace pattern:
+${replace;v1.0,v2.0,v3.0;^v;version-}
+# Returns: "version-1.0,version-2.0,version-3.0"
+Using back-references:
+${replace;com.example.api,com.example.impl;com\.example\.(.+);$1}
+# Returns: "api,impl"
+Custom delimiter:
+${replace;a,b,c;$;-suffix;;}
+# Returns: "a-suffix;b-suffix;c-suffix"
+${replacelist}${replacelist} for quoted section support${replacestring} for single string replacementSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/replacelist.html b/org.bndtools.help/docs/macros/replacelist.html new file mode 100644 index 0000000000..5d3bdd6879 --- /dev/null +++ b/org.bndtools.help/docs/macros/replacelist.html @@ -0,0 +1,67 @@ + + + + +The replacelist macro applies regex-based replacement to each element in a list. Unlike ${replace}, it uses a sophisticated splitter that handles quoted sections, preserving commas within quotes.
${replacelist;<list>;<regex>[;<replacement>[;<delimiter>]]}
+list - List of elements (can contain quoted sections with commas)regex - Regular expression pattern to matchreplacement (optional) - Replacement string with $1-$9 back-references (default: empty)delimiter (optional) - Output delimiter (default: ",")element.replaceAll(regex, replacement) to each$1, $2, etc.)Add to quoted elements:
+impls: foo;version="[1,2)", bar;version="[1.2,2)"
+${replacelist;${list;impls};$;\\;strategy=highest}
+# Returns: foo;version="[1,2)";strategy=highest,bar;version="[1.2,2)";strategy=highest
+Process dependencies:
+deps: lib;version="1.0,2.0",tool;version="2.0"
+${replacelist;${deps};$;,optional}
+Back-references with quotes:
+${replacelist;a="x,y",b="z";(.+)="(.+)";$1:$2}
+${replace} for OSGi attributes${replace} for simple splitting${replacestring} for single stringsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/replacestring.html b/org.bndtools.help/docs/macros/replacestring.html new file mode 100644 index 0000000000..30f5e19843 --- /dev/null +++ b/org.bndtools.help/docs/macros/replacestring.html @@ -0,0 +1,77 @@ + + + + +The replacestring macro applies regex-based replacement to a single string, supporting back-references for captured groups.
${replacestring;<string>;<regex>[;<replacement>]}
+string - The string to processregex - Regular expression pattern to matchreplacement (optional) - Replacement string with $1-$9 back-references (default: empty)string.replaceAll(regex, replacement)$1, $2, etc.)Simple replacement:
+description: This is, possibly, the best implementation ever!
+${replacestring;${description};possibly;definitely}
+# Returns: "This is, definitely, the best implementation ever!"
+Remove pattern:
+${replacestring;version-1.2.3;version-;}
+# Returns: "1.2.3"
+Using back-references:
+${replacestring;com.example.impl;com\.(.+)\.impl;$1}
+# Returns: "example"
+Multiple replacements:
+${replacestring;hello world;[aeiou];*}
+# Returns: "h*ll* w*rld"
+Path normalization:
+${replacestring;${path};\\;/}
+# Replace backslashes with forward slashes
+${replace} or ${replacelist}${replace} for list processing${subst} for simple string substitutionSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/repo.html b/org.bndtools.help/docs/macros/repo.html new file mode 100644 index 0000000000..d324d72578 --- /dev/null +++ b/org.bndtools.help/docs/macros/repo.html @@ -0,0 +1,25 @@ + + + + +Returns the absolute file system paths to the specified artifacts in the repositories.
+BSN is be a comma-separated list of bundle symbolic names. If the artifact +is not a bundle, then the synthetic bundle symbolic names of groupId:artifactId +can be used. Normally only a single bundle symbolic name is used since the remainder of the options apply to all the bundle symbolic names.
+VERSION is a OSGi version-range for the artifact. Special values supported are:
+* project - This return the built artifact from a project in the Bnd workspace.
+* snapshot - Synonym for project.
+* latest - The highest version available in a project in the Bnd workspace or the repositories. The built artifact from a project in the Bnd workspace is always used if it exists under the assumption the Bnd workspace is always building the latest version of the artifact.
+If the version range is not specified, the version range [0,∞) is used.
Note about version-range: It is important to know that this is needs to be a version range according to OSGi version semantics (not Maven! version). That means for example, to refer to the maven version 4.6.0-dcm you have to write 4.6.0.-dcm which then internally will be translated to the maven equivalent if you are using e.g. MavenBndRepository.
STRATEGY is the selection strategy to be used when multiple artifacts with the bundle symbolic name exist within the version range. The strategies supported are:
+* HIGHEST - The highest version for the artifact which is included by the version range. This is the default strategy and is the strategy always used by the special version range latest.
+* LOWEST - The lowest version for the artifact which is included by the version range.
+* EXACT - When this strategy is used, the version range must be a single version which is the version which is searched for. If multiple repositories contain the the exact version of the artifact, the artifact from the first repository is used.
The repodigests macro returns hexadecimal cryptographic digests representing the contents of all repositories or specific named repositories. This is useful for detecting changes in repository state.
${repodigests[;<repo-name>...]}
+repo-name (optional) - One or more repository names. If omitted, returns digests for all repositories.getDigest() methodGet all repository digests:
+${repodigests}
+# Returns: "a1b2c3d4e5f6...,f6e5d4c3b2a1..."
+Specific repositories:
+${repodigests;Maven Central;Local}
+Check for changes:
+repo.state=${repodigests}
+Conditional on digest:
+${if;${repodigests;Release};has-digest;no-digest}
+${repos} for repository namesTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/repos.html b/org.bndtools.help/docs/macros/repos.html new file mode 100644 index 0000000000..8df4aff899 --- /dev/null +++ b/org.bndtools.help/docs/macros/repos.html @@ -0,0 +1,63 @@ + + + + +The repos macro returns a comma-separated list of the names of all repositories configured in the current project or workspace.
${repos}
+None - this macro takes no parameters.
+Get all repository names:
+${repos}
+# Returns: "Maven Central, Local, Workspace, Release" (example)
+Document available repositories:
+Bundle-RepositoriesUsed: ${repos}
+Count repositories:
+repo.count=${size;${split;,;${repos}}}
+Check for specific repository:
+${if;${findlast;${repos};Maven Central};has-central;no-central}
+${repo} for accessing repository contentscnf/build.bnd or project filesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/retainall.html b/org.bndtools.help/docs/macros/retainall.html new file mode 100644 index 0000000000..d6d8abffce --- /dev/null +++ b/org.bndtools.help/docs/macros/retainall.html @@ -0,0 +1,87 @@ + + + + +Keep only elements from the first list that are also present in the second list, effectively computing the intersection of two lists.
+${retainall;<list1>;<list2>}
+# Find common packages
+packages1 = com.example.api, com.example.impl, com.example.util
+packages2 = com.example.api, com.example.util, com.other.api
+common-packages = ${retainall;${packages1};${packages2}}
+# Returns: com.example.api,com.example.util
+
+# Keep only approved dependencies
+all-deps = foo.jar, bar.jar, baz.jar, qux.jar
+approved-deps = foo.jar, baz.jar
+used-deps = ${retainall;${all-deps};${approved-deps}}
+# Returns: foo.jar,baz.jar
+
+# List intersection
+list-a = a, b, c, d
+list-b = c, d, e, f
+intersection = ${retainall;${list-a};${list-b}}
+# Returns: c,d
+
+# Filter build path to allowed items
+buildpath = ${workspace.buildpath}
+allowed-libs = commons-io, commons-lang, slf4j-api
+-buildpath: ${retainall;${buildpath};${allowed-libs}}
+
+# No common elements
+list-x = a, b, c
+list-y = d, e, f
+${retainall;${list-x};${list-y}}
+# Returns: (empty)
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/reverse.html b/org.bndtools.help/docs/macros/reverse.html new file mode 100644 index 0000000000..8cc250b9d7 --- /dev/null +++ b/org.bndtools.help/docs/macros/reverse.html @@ -0,0 +1,67 @@ + + + + +The reverse macro reverses the order of elements from one or more combined lists. The last element becomes first, and the first becomes last.
${reverse;<list>[;<list>...]}
+list - One or more semicolon-separated lists (items within lists are comma-separated)Reverse a simple list:
+${reverse;1,2,3,4,5}
+# Returns: "5,4,3,2,1"
+Reverse text items:
+${reverse;apple,banana,cherry}
+# Returns: "cherry,banana,apple"
+Reverse multiple lists:
+${reverse;red,green;blue,yellow}
+# Returns: "yellow,blue,green,red"
+Reverse file list:
+${reverse;${lsr;src;*.java}}
+# Returns files in reverse order
+${sort} for descending orderSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/select.html b/org.bndtools.help/docs/macros/select.html new file mode 100644 index 0000000000..031fb88e3b --- /dev/null +++ b/org.bndtools.help/docs/macros/select.html @@ -0,0 +1,70 @@ + + + + +Filter a list to include only elements that match a regular expression. This macro is an alias for the filter macro.
+${select;<list>;<regex>}
+Returns a new list containing only the elements from the input list that match the specified regular expression pattern.
+This macro is functionally identical to filter.
+# Select items starting with "com."
+packages = com.example.api, org.sample.impl, com.example.util
+${select;${packages};com\..*}
+# Returns: com.example.api,com.example.util
+
+# Select files with specific extension
+files = foo.jar, bar.txt, baz.jar
+${select;${files};.*\.jar}
+# Returns: foo.jar,baz.jar
+
+# Select numbered items
+items = item1, item2, other, item3
+${select;${items};item\d+}
+# Returns: item1,item2,item3
+
+# Select packages matching pattern
+-conditionalpackage: ${select;${packages};com\.company\.internal\..*}
+.* for partial matching)TODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/separator.html b/org.bndtools.help/docs/macros/separator.html new file mode 100644 index 0000000000..f4b22a28bf --- /dev/null +++ b/org.bndtools.help/docs/macros/separator.html @@ -0,0 +1,61 @@ + + + + +The separator macro returns the operating system's file separator character used in file paths.
${separator}
+None - this macro takes no parameters.
+/ (forward slash) on Unix/Linux/Mac systems\ (backslash) on Windows systemsFile.separator in JavaGet the file separator:
+${separator}
+# Returns: "/" on Unix, "\" on Windows
+Build paths manually:
+path=${basedir}${separator}src${separator}main${separator}java
+Use in conditionals:
+${if;${equals;${separator};/};unix;windows}
+Replace separators:
+${replacestring;${path};${separator};/}
+/ or \${pathseparator}${osfile} for OS-specific path creation${pathseparator} for path list separator (: or ;)See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/sha1.html b/org.bndtools.help/docs/macros/sha1.html new file mode 100644 index 0000000000..3c4ba90b57 --- /dev/null +++ b/org.bndtools.help/docs/macros/sha1.html @@ -0,0 +1,69 @@ + + + + +The sha1 macro calculates the SHA-1 hash of a resource that exists within the current bundle JAR. The result can be returned as either Base64 (default) or hexadecimal encoding.
${sha1;<resource-path>[;<encoding>]}
+resource-path - Path to a resource within the bundle (e.g., "META-INF/MANIFEST.MF")encoding (optional) - Output encoding: "hex" for hexadecimal, "base64" for Base64 (default)Get SHA-1 of a resource (Base64):
+${sha1;META-INF/services/com.example.Service}
+# Returns Base64-encoded SHA-1
+Get SHA-1 in hexadecimal format:
+${sha1;config/application.properties;hex}
+# Returns: "356a192b7913b04c54574d18c28d46e6395428ab" (example)
+Hash manifest file:
+manifest.sha1=${sha1;META-INF/MANIFEST.MF;hex}
+Verify resource integrity:
+Bundle-ResourceSHA1: ${sha1;important-config.xml}
+${md5} and ${digest} for other hash algorithmsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/size.html b/org.bndtools.help/docs/macros/size.html new file mode 100644 index 0000000000..1478054e11 --- /dev/null +++ b/org.bndtools.help/docs/macros/size.html @@ -0,0 +1,65 @@ + + + + +The size macro counts and returns the total number of elements across one or more comma-separated lists.
${size;<list>[;<list>...]}
+list - One or more comma-separated lists to countCount elements in a list:
+${size;a,b,c,d}
+# Returns: 4
+Count multiple lists:
+${size;red,green;blue,yellow}
+# Returns: 4
+Count packages:
+package.count=${size;${packages}}
+Conditional on list size:
+${if;${size;${exports}};has-exports;no-exports}
+${length} for string lengthSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/sjoin.html b/org.bndtools.help/docs/macros/sjoin.html new file mode 100644 index 0000000000..900d1a28e7 --- /dev/null +++ b/org.bndtools.help/docs/macros/sjoin.html @@ -0,0 +1,74 @@ + + + + +The sjoin macro combines one or more lists into a single string using a custom separator. Unlike ${join} which always uses commas, this allows you to specify any separator.
${sjoin;<separator>;<list>[;<list>...]}
+separator - The string to use between elements (can be any string)list - One or more lists to combineJoin with space:
+${sjoin; ;apple,banana,cherry}
+# Returns: "apple banana cherry"
+Join with custom separator:
+${sjoin; | ;red,green,blue}
+# Returns: "red | green | blue"
+Join with newline:
+${sjoin;\n;line1,line2,line3}
+# Returns multi-line text
+Join multiple lists with colon:
+${sjoin;:;${exports};${imports}}
+# Returns packages separated by colons
+Create formatted list:
+${sjoin;, and ;first,second,third}
+# Returns: "first, and second, and third"
+${join} for comma-separated joining${path} for OS-specific path separator joiningSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/sort.html b/org.bndtools.help/docs/macros/sort.html new file mode 100644 index 0000000000..9537361f67 --- /dev/null +++ b/org.bndtools.help/docs/macros/sort.html @@ -0,0 +1,66 @@ + + + + +The sort macro combines one or more lists and sorts their contents alphabetically (lexicographically). The sorting is case-sensitive with uppercase letters before lowercase.
${sort;<list>[;<list>...]}
+list - One or more semicolon-separated lists to sortSort a simple list:
+${sort;cherry,apple,banana}
+# Returns: "apple,banana,cherry"
+Sort with mixed case:
+${sort;Zebra,apple,Ant,banana}
+# Returns: "Ant,Zebra,apple,banana"
+Sort multiple lists:
+${sort;red,green;blue,yellow}
+# Returns: "blue,green,red,yellow"
+Sort and filter:
+${sort;${filter;${packages};com\.example\..*}}
+${nsort}${nsort} for numeric sorting${reverse} to reverse orderSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/split.html b/org.bndtools.help/docs/macros/split.html new file mode 100644 index 0000000000..21040460bf --- /dev/null +++ b/org.bndtools.help/docs/macros/split.html @@ -0,0 +1,72 @@ + + + + +The split macro splits one or more strings into a list using a regular expression as the delimiter. Empty segments are filtered out.
${split;<regex>[;<string>...]}
+regex - Regular expression pattern to split onstring - One or more strings to splitSplit on comma:
+${split;,;red,green,blue}
+# Returns: "red,green,blue"
+Split on whitespace:
+${split;\s+;one two three four}
+# Returns: "one,two,three,four"
+Split on pipe:
+${split;\|;apple|banana|cherry}
+# Returns: "apple,banana,cherry"
+Split multiple strings:
+${split;:;path1:path2;path3:path4}
+# Returns: "path1,path2,path3,path4"
+Split path on separator:
+${split;${pathseparator};${some.path}}
+${join} and ${sjoin} for combining${filter} for pattern-based filteringSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/startswith.html b/org.bndtools.help/docs/macros/startswith.html new file mode 100644 index 0000000000..e590f8a086 --- /dev/null +++ b/org.bndtools.help/docs/macros/startswith.html @@ -0,0 +1,66 @@ + + + + +The startswith macro checks if a given string starts with a specified prefix. Returns the string if it matches, empty string otherwise.
${startswith;<string>;<prefix>}
+string - The string to checkprefix - The prefix to look forCheck string prefix:
+${startswith;com.example.api;com.example}
+# Returns: "com.example.api"
+Check with non-matching prefix:
+${startswith;org.other.package;com.example}
+# Returns: ""
+Use in conditional:
+${if;${startswith;${project};test.};test-project;regular-project}
+Filter list by prefix:
+${filter;${packages};${startswith;.*;com\.example}}
+${if} for conditional logic${endswith} for suffix checking${matches} for regex matchingSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/stem.html b/org.bndtools.help/docs/macros/stem.html new file mode 100644 index 0000000000..9824c52933 --- /dev/null +++ b/org.bndtools.help/docs/macros/stem.html @@ -0,0 +1,73 @@ + + + + +The stem macro returns the portion of a string up to (but not including) the first dot character. If no dot is found, returns the entire string.
${stem;<string>}
+string - The string to process.) characterExtract file basename:
+${stem;example.txt}
+# Returns: "example"
+Get package prefix:
+${stem;com.example.api}
+# Returns: "com"
+Extract version major:
+${stem;1.2.3}
+# Returns: "1"
+No dot present:
+${stem;filename}
+# Returns: "filename"
+Multiple dots:
+${stem;archive.tar.gz}
+# Returns: "archive" (only first dot)
+${basename} for file name extractionSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/sublist.html b/org.bndtools.help/docs/macros/sublist.html new file mode 100644 index 0000000000..dd7f1d8516 --- /dev/null +++ b/org.bndtools.help/docs/macros/sublist.html @@ -0,0 +1,77 @@ + + + + +The sublist macro extracts elements from a list between start and end positions. It supports negative indices to count from the end of the list.
${sublist;<start>;<end>;<list>[;<list>...]}
+start - Starting index (0-based, negative counts from end)end - Ending index (exclusive, negative counts from end)list - One or more lists to combine and extract fromGet first 3 elements:
+${sublist;0;3;apple,banana,cherry,date,elderberry}
+# Returns: "apple,banana,cherry"
+Get last 2 elements:
+${sublist;-2;-0;apple,banana,cherry}
+# Returns: "banana,cherry"
+Skip first element:
+${sublist;1;-0;red,green,blue}
+# Returns: "green,blue"
+Get middle elements:
+${sublist;2;5;a,b,c,d,e,f,g}
+# Returns: "c,d,e"
+Extract from multiple lists:
+${sublist;0;3;one,two;three,four,five}
+# Returns: "one,two,three"
+${first}, ${last}, ${get} for single elementsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/subst.html b/org.bndtools.help/docs/macros/subst.html new file mode 100644 index 0000000000..dd8314f232 --- /dev/null +++ b/org.bndtools.help/docs/macros/subst.html @@ -0,0 +1,96 @@ + + + + +Perform regex-based substring substitution on a target string, with optional replacement limit.
+${subst;<target>;<regex>[;<replacement>[;<count>]]}
+The macro:
+1. Finds all substrings in target that match regex
+2. Replaces them with replacement (or empty string if not specified)
+3. Limits replacements to count if specified (otherwise replaces all)
+4. Returns the modified string
This is similar to Java's String.replaceAll() but with optional replacement count.
# Remove file extension
+${subst;foo.bar;\.bar}
+# Returns: foo
+
+# Replace dots with dashes
+${subst;com.example.package;\.;-}
+# Returns: com-example-package
+
+# Replace with specified text
+${subst;Hello World;World;Universe}
+# Returns: Hello Universe
+
+# Limit replacements
+${subst;a,b,c,d;,;-;2}
+# Returns: a-b-c,d (only first 2 replacements)
+
+# Remove all digits
+${subst;version1.2.3;\d}
+# Returns: version.. (empty replacement)
+
+# Replace spaces with underscores
+${subst;My Project Name; ;_}
+# Returns: My_Project_Name
+
+# Complex regex - remove version info
+${subst;bundle-1.0.0.jar;-[\d.]+\.jar;.jar}
+# Returns: bundle.jar
+
+# Multiple occurrences
+${subst;test-test-test;test;prod}
+# Returns: prod-prod-prod
+
+# With capture groups
+${subst;com.example.api;com\.(.*)\.api;org.$1.impl}
+# Returns: org.example.impl
+$1, $2, etc.)$ in replacement, escape it as $$See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/substring.html b/org.bndtools.help/docs/macros/substring.html new file mode 100644 index 0000000000..0993984625 --- /dev/null +++ b/org.bndtools.help/docs/macros/substring.html @@ -0,0 +1,76 @@ + + + + +The substring macro extracts a portion of a string between start and end positions. It supports negative indices to count from the end of the string.
${substring;<string>;<start>[;<end>]}
+string - The source stringstart - Starting index (0-based, negative counts from end)end (optional) - Ending index (exclusive, negative counts from end, default: end of string)Get first 5 characters:
+${substring;hello world;0;5}
+# Returns: "hello"
+Get last 5 characters:
+${substring;hello world;-5}
+# Returns: "world"
+Remove first 2 characters:
+${substring;hello;2}
+# Returns: "llo"
+Extract middle portion:
+${substring;hello world;6;11}
+# Returns: "world"
+Use negative indices:
+${substring;hello world;-5;-1}
+# Returns: "worl"
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/sum.html b/org.bndtools.help/docs/macros/sum.html new file mode 100644 index 0000000000..36ebe9dbd0 --- /dev/null +++ b/org.bndtools.help/docs/macros/sum.html @@ -0,0 +1,65 @@ + + + + +The sum macro calculates the sum of all numeric values provided in one or more lists. Each element is parsed as a double-precision floating-point number.
${sum;<list>[;<list>...]}
+list - One or more semicolon-separated lists of numeric valuesSum a simple list:
+${sum;1,2,3,4,5}
+# Returns: "15"
+Sum multiple lists:
+${sum;10,20;30,40}
+# Returns: "100"
+Sum decimal values:
+${sum;1.5,2.5,3.5}
+# Returns: "7.5"
+Calculate total from properties:
+total=${sum;${value1};${value2};${value3}}
+${average} for mean calculation${max} and ${min} for extremesSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/system.html b/org.bndtools.help/docs/macros/system.html new file mode 100644 index 0000000000..b0f049ffe6 --- /dev/null +++ b/org.bndtools.help/docs/macros/system.html @@ -0,0 +1,76 @@ + + + + +The system macro executes an operating system command and returns its standard output. The command runs in the project's base directory. The build fails if the command returns a non-zero exit code.
${system;<command>[;<input>]}
+command - The system command to executeinput (optional) - Text to send to the command's standard inputcmd /cGet Git commit hash:
+Git-Commit: ${system;git rev-parse HEAD}
+Get current date:
+Build-Date: ${system;date}
+Execute command with input:
+${system;wc -l;line1\nline2\nline3}
+Get Maven version:
+mvn.version=${system;mvn --version}
+List files:
+files=${system;ls -la}
+cmd /c${system-allow-fail} for non-failing variantTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/system_allow_fail.html b/org.bndtools.help/docs/macros/system_allow_fail.html new file mode 100644 index 0000000000..c5a4bb4870 --- /dev/null +++ b/org.bndtools.help/docs/macros/system_allow_fail.html @@ -0,0 +1,72 @@ + + + + +The system-allow-fail macro executes an operating system command and returns its output, but unlike ${system}, it does not fail the build if the command returns a non-zero exit code. Failed commands generate a warning instead.
${system-allow-fail;<command>[;<input>]}
+command - The system command to executeinput (optional) - Text to send to the command's standard inputcmd /cTry to get Git info (may not be in repo):
+Git-Branch: ${system-allow-fail;git branch --show-current}
+Optional version check:
+tool.version=${system-allow-fail;tool --version}
+Check for optional tool:
+${if;${system-allow-fail;which docker};docker-available;docker-not-found}
+Get info with fallback:
+host=${def;${system-allow-fail;hostname};unknown-host}
+${system}${system} for failing variantTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/template.html b/org.bndtools.help/docs/macros/template.html new file mode 100644 index 0000000000..7bc0d4c1d4 --- /dev/null +++ b/org.bndtools.help/docs/macros/template.html @@ -0,0 +1,52 @@ + + + + +The template macro is intended to make it very simple to generate new information based on a Parameters. Parameters
+are the Bnd workhorse to store information. The macro takes the following arguments:
macro name The name of the macro (not the value)
+template+ the templates (these are joined with the ';' as separator)
+The template is expanded for each entry of the Parameters. They key can be referred by ${@} and the attributes can be
+referred by ${@<name>}, where the name is the name of the attribute. All entries are then joined with a comma (,) as
+separator.
For example, the following example shows how to extract and attribute as a list:
+bnd shell
+> parameters = key;attr=1, key;attr="2"
+> ${template;parameters;${@attr}}
+1,2
+The template macro takes the NAME of the macro that contains the value. I.e. it does not take the expanded value as
+argument. The reason is that the referred macro gets merged and decorated. Merge takes all properties that start with the given name.
> parameters = key;attr=1, key;attr="2"
+> parameters.extra = KEY;attr=3, KEY;attr="4"
+> ${template;parameters;${@attr}}
+1,2,3,4
+Decorate means that the property with the same name but with a + at the end will be matched with the value. In this property the
+key is a glob. It is matched against the key from the original merged properties. Any matching properties get the attributes
+from the decorator.
> parameters = a;attr=1, b;attr="2"
+> parameters.extra = c;attr=3, d;attr="4"
+> parameters+ = (c|d);attr=X
+> ${template;parameters;${@attr}}
+1,2,X,X
+The macro accepts any number of arguments after the macro name. These values are joined with a semi-colon as separator.
+The reason is that then the ; in the templates do not have to be escaped:
bnd shell
+> parameters = a;attr=1, b;attr="2"
+> ${template;parameters;${@};key=${@};${@}=${@attr}}
+a;a=1,b;b=2
+The thisfile macro returns the absolute path to the properties file being processed. This provides the full path to the current .bnd, .bndrun, or other bnd configuration file.
${thisfile}
+None - this macro takes no parameters.
+Get current file path:
+Current-Config: ${thisfile}
+# Returns: "/path/to/project/bnd.bnd"
+Use in logging:
+Processing: ${thisfile}
+Reference in documentation:
+# Configuration defined in: ${thisfile}
+Conditional based on file:
+${if;${endswith;${thisfile};test.bnd};test-mode;normal-mode}
+${basedir} for the project directory${propertiesname} for just the filename${propertiesdir} for just the directorySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/toclaspath.html b/org.bndtools.help/docs/macros/toclaspath.html new file mode 100644 index 0000000000..11eb49edd4 --- /dev/null +++ b/org.bndtools.help/docs/macros/toclaspath.html @@ -0,0 +1,67 @@ + + + + +The toclasspath macro converts fully qualified class names to file paths by replacing dots with path separators and optionally adding .class extension.
${toclasspath;<class-names>[;<add-extension>]}
+class-names - Comma-separated list of fully qualified class namesadd-extension (optional) - Boolean to add .class extension (default: true).) with path separators (/).class extension (default: yes)Convert class names with extension:
+${toclasspath;com.example.Main,com.example.Util}
+# Returns: "com/example/Main.class,com/example/Util.class"
+Convert without extension:
+${toclasspath;org.test.TestCase;false}
+# Returns: "org/test/TestCase"
+Create paths for lookup:
+paths=${toclasspath;${classes};true}
+Package paths without extension:
+${toclasspath;com.example.api;false}
+# Returns: "com/example/api"
+/ as separator${toclassname}${toclassname} for reverse operationSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/toclassname.html b/org.bndtools.help/docs/macros/toclassname.html new file mode 100644 index 0000000000..d1228026b0 --- /dev/null +++ b/org.bndtools.help/docs/macros/toclassname.html @@ -0,0 +1,68 @@ + + + + +The toclassname macro converts a list of file paths (.class or .java files) to fully qualified class names by removing the extension and converting path separators to dots.
${toclassname;<file-paths>}
+file-paths - Comma-separated list of file paths ending in .class or .java.class or .java extension/) to dots (.)Convert class file paths:
+${toclassname;com/example/Main.class,com/example/Util.class}
+# Returns: "com.example.Main,com.example.Util"
+Convert Java source paths:
+${toclassname;org/test/TestCase.java}
+# Returns: "org.test.TestCase"
+Use with file lists:
+classes=${toclassname;${lsr;bin;*.class}}
+Mixed extensions:
+${toclassname;com/Foo.java,com/Bar.class}
+# Returns: "com.Foo,com.Bar"
+.class and .java files/ (converted to .)${toclasspath} for inverse operationSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/tolower.html b/org.bndtools.help/docs/macros/tolower.html new file mode 100644 index 0000000000..caa9921945 --- /dev/null +++ b/org.bndtools.help/docs/macros/tolower.html @@ -0,0 +1,64 @@ + + + + +The tolower macro converts a string to lowercase using the default locale.
${tolower;<string>}
+string - The string to convert to lowercaseConvert simple string:
+${tolower;HELLO WORLD}
+# Returns: "hello world"
+Convert mixed case:
+${tolower;MyClassName}
+# Returns: "myclassname"
+Normalize for comparison:
+${if;${equals;${tolower;${input}};${tolower;${expected}}};match;no-match}
+Convert package names:
+normalized=${tolower;Com.Example.API}
+# Returns: "com.example.api"
+${toupper} for uppercase conversionSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/toupper.html b/org.bndtools.help/docs/macros/toupper.html new file mode 100644 index 0000000000..ee06dd2160 --- /dev/null +++ b/org.bndtools.help/docs/macros/toupper.html @@ -0,0 +1,63 @@ + + + + +The toupper macro converts a string to uppercase using the default locale.
${toupper;<string>}
+string - The string to convert to uppercaseConvert simple string:
+${toupper;hello world}
+# Returns: "HELLO WORLD"
+Convert mixed case:
+${toupper;MyClassName}
+# Returns: "MYCLASSNAME"
+Create constants:
+constant.name=${toupper;${property.name}}
+Format identifiers:
+Bundle-Id: ${toupper;${bsn}}
+${tolower} for lowercase conversionSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/trim.html b/org.bndtools.help/docs/macros/trim.html new file mode 100644 index 0000000000..45f031ad5a --- /dev/null +++ b/org.bndtools.help/docs/macros/trim.html @@ -0,0 +1,68 @@ + + + + +The trim macro removes leading and trailing whitespace from a string. It uses Java's String.trim() method, which removes spaces, tabs, newlines, and other whitespace characters.
${trim;<string>}
+string - The string to trimRemove extra spaces:
+${trim; hello world }
+# Returns: "hello world"
+Clean up property value:
+cleaned.value=${trim;${some.property}}
+Trim after concatenation:
+${trim;${first} ${second} }
+Process multiline value:
+${trim;
+ some value
+}
+# Returns: "some value"
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/tstamp.html b/org.bndtools.help/docs/macros/tstamp.html new file mode 100644 index 0000000000..10e5538cec --- /dev/null +++ b/org.bndtools.help/docs/macros/tstamp.html @@ -0,0 +1,82 @@ + + + + +The tstamp macro creates a formatted timestamp string using a custom date format pattern, timezone, and optional timestamp value.
${tstamp[;<format>[;<timezone>[;<millis>]]]}
+format (optional) - Date format pattern (default: "yyyyMMddHHmm")timezone (optional) - Timezone ID (default: "UTC")millis (optional) - Timestamp in milliseconds (default: current time)Default format (UTC):
+${tstamp}
+# Returns: "202411201030" (example)
+Custom format:
+${tstamp;yyyy-MM-dd HH:mm:ss}
+# Returns: "2024-11-20 10:30:45"
+With timezone:
+${tstamp;yyyy-MM-dd HH:mm;America/New_York}
+# Returns: "2024-11-20 05:30"
+ISO 8601 format:
+${tstamp;yyyy-MM-dd'T'HH:mm:ss'Z'}
+# Returns: "2024-11-20T10:30:45Z"
+Specific timestamp:
+${tstamp;yyyy-MM-dd;;1700000000000}
+# Formats the provided timestamp
+Build timestamp in manifest:
+Build-Timestamp: ${tstamp;yyyy-MM-dd HH:mm:ss z;UTC}
+${currenttime}${long2date}${currenttime}, ${long2date}, ${now}See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/unescape.html b/org.bndtools.help/docs/macros/unescape.html new file mode 100644 index 0000000000..de1e4d9d6c --- /dev/null +++ b/org.bndtools.help/docs/macros/unescape.html @@ -0,0 +1,76 @@ + + + + +The unescape macro converts escape sequences in strings to their corresponding control characters. It concatenates all input arguments and processes escape sequences.
${unescape;<string>[;<string>...]}
+string - One or more strings to unescape (concatenated before processing)\n → newline (line feed)\r → carriage return\t → tab\b → backspace\f → form feedUnescape newline:
+${unescape;line1\nline2}
+# Returns: "line1
+# line2"
+Unescape tab:
+${unescape;col1\tcol2\tcol3}
+# Returns: "col1 col2 col3"
+Multiple strings:
+${unescape;first\n;second\n;third}
+# Returns: "first
+# second
+# third"
+Mixed escapes:
+${unescape;Name:\tJohn\nAge:\t30}
+TODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/uniq.html b/org.bndtools.help/docs/macros/uniq.html new file mode 100644 index 0000000000..dd63e29f98 --- /dev/null +++ b/org.bndtools.help/docs/macros/uniq.html @@ -0,0 +1,68 @@ + + + + +The uniq macro combines one or more lists and removes all duplicate elements, preserving insertion order (first occurrence is kept).
${uniq;<list>[;<list>...]}
+list - One or more semicolon-separated lists to processRemove duplicates from single list:
+${uniq;1,2,3,1,2,4}
+# Returns: "1,2,3,4"
+Remove duplicates from multiple lists:
+${uniq;red,green,blue;red,yellow}
+# Returns: "red,green,blue,yellow"
+Clean package list:
+${uniq;${exports};${imports}}
+# Returns unique packages
+Deduplicate configuration:
+unique.values=${uniq;${list1};${list2};${list3}}
+${sort} to also sort the result${removeall} and ${retainall} for set operationsSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/uri.html b/org.bndtools.help/docs/macros/uri.html new file mode 100644 index 0000000000..4b4f67e985 --- /dev/null +++ b/org.bndtools.help/docs/macros/uri.html @@ -0,0 +1,92 @@ + + + + +Resolve a URI against a base URI, handling relative URIs and file scheme URIs appropriately.
+${uri;<uri>[;<base-uri>]}
+The macro resolves URIs based on these rules:
+1. If the URI is absolute (has a scheme like http:, https:) and not a file: scheme, returns it unchanged
+2. If the URI is relative or uses the file: scheme:
+ - Resolves it against the specified base URI (if provided)
+ - Or resolves it against the processor's base URI (project or workspace root)
+3. Returns the resolved URI
The base URI depends on context when not explicitly provided:
+- In a project's bnd.bnd: Project directory URI
+- In a workspace's cnf/build.bnd: Workspace directory URI
# Current directory URI
+${uri;.}
+# In project: file:///workspace/my.project/
+# In workspace: file:///workspace/
+
+# Resolve relative path
+${uri;src/main/resources}
+# Returns: file:///project/base/src/main/resources
+
+# Resolve against custom base
+${uri;config/settings.xml;file:///opt/app/}
+# Returns: file:///opt/app/config/settings.xml
+
+# Absolute HTTP URI (returned unchanged)
+${uri;https://example.com/resource}
+# Returns: https://example.com/resource
+
+# File URI (resolved against base)
+${uri;file:./local/path}
+# Returns: file:///project/base/local/path
+
+# Parent directory reference
+${uri;../config}
+# Resolved relative to base
+
+# Use in configuration
+Bundle-DocURL: ${uri;docs/index.html;${Bundle-Site}}
+
+# Reference workspace files
+config-location = ${uri;cnf/config.properties}
+file:) are returned unchangedfile: scheme URIs are resolved against base. (current) and .. (parent) directory referencesTODO Needs review - AI Generated content
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/user.html b/org.bndtools.help/docs/macros/user.html new file mode 100644 index 0000000000..5289227d96 --- /dev/null +++ b/org.bndtools.help/docs/macros/user.html @@ -0,0 +1,12 @@ + + + + +The user macro is an alias to the global macro.
+ + diff --git a/org.bndtools.help/docs/macros/vcompare.html b/org.bndtools.help/docs/macros/vcompare.html new file mode 100644 index 0000000000..630eb7185b --- /dev/null +++ b/org.bndtools.help/docs/macros/vcompare.html @@ -0,0 +1,97 @@ + + + + +Compare two OSGi version strings using proper version semantics, returning -1, 0, or 1.
+${vcompare;<version1>;<version2>}
+Compares two OSGi version strings using semantic version comparison and returns:
+- 0 - The versions are equal
+- 1 - The first version is greater than the second (newer)
+- -1 - The first version is less than the second (older)
OSGi versions follow the format: major.minor.micro.qualifier
+- Numeric parts (major, minor, micro) are compared numerically
+- Qualifier is compared lexicographically
# Equal versions
+${vcompare;1.0.0;1.0.0}
+# Returns: 0
+
+# First version greater
+${vcompare;2.0.0;1.5.0}
+# Returns: 1
+
+# First version less
+${vcompare;1.0.0;2.0.0}
+# Returns: -1
+
+# Micro version comparison
+${vcompare;1.2.3;1.2.2}
+# Returns: 1
+
+# With qualifiers
+${vcompare;1.0.0.SNAPSHOT;1.0.0.RELEASE}
+# Returns: 1 (SNAPSHOT > RELEASE lexicographically)
+
+# Different lengths
+${vcompare;1.0;1.0.0}
+# Returns: 0 (1.0 equals 1.0.0.0)
+
+# Use in conditional logic
+-include ${if;${vcompare;${bndversion};7.0.0};modern.bnd;legacy.bnd}
+
+# Check minimum version requirement
+-include ${if;${vcompare;${Bundle-Version};2.0.0};compatible.bnd;incompatible.bnd}
+
+# Version range check
+is-current = ${if;${vcompare;${Bundle-Version};3.0.0};true;false}
+
+# Compare with project version
+${vcompare;${version;===;${Bundle-Version}};${minimum-version}}
+See test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/version.html b/org.bndtools.help/docs/macros/version.html new file mode 100644 index 0000000000..0279bd2a79 --- /dev/null +++ b/org.bndtools.help/docs/macros/version.html @@ -0,0 +1,12 @@ + + + + +The version macro is an alias to the versionmask macro for backward compatibility reasons.
+ + diff --git a/org.bndtools.help/docs/macros/version_cleanup.html b/org.bndtools.help/docs/macros/version_cleanup.html new file mode 100644 index 0000000000..29ad0b467a --- /dev/null +++ b/org.bndtools.help/docs/macros/version_cleanup.html @@ -0,0 +1,22 @@ + + + + +The version_cleanup macro takes a version-ish string and cleans it up, producing the OSGi Version syntax.
For example, a Maven version can be turned into the OSGi Version syntax:
+${version_cleanup;1.2.3-SNAPSHOT} -> 1.2.3.SNAPSHOT
+null, the version returned is 0.(\\(|\\[)\\s*([-.\\w]+)\\s*,\\s*([-.\\w]+)\\s*(\\]|\\)) (with java.util.regex.Pattern.DOTALL enabled) a sufficiently cleaned up OSGi Version range is returned.(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^\\p{Alnum}](.*))? (with java.util.regex.Pattern.DOTALL enabled) a sufficiently cleaned up OSGi Version string is returned.The versionmask macro takes a template (MASK) and a version. It then uses the template
+to modify the version. This is useful to get rid of parts of the version. For example,
+to get rid of the qualifier the following macro is useful:
${versionmask;===;1.2.3.awfulqualifier} -> 1.2.3
+It is also possible to modify the template. For example, if the next minor version is +sought, then the following macro is useful:
+${versionmask;=+;1.2.3.awfulqualifier} -> 1.3
+The syntax for the versionmask macro is:
versionmask ::= mask
+mask ::= m ( m ( m ( q )? )? )?
+m ::= [0-9=+-~]
+q ::= [0-9=~Ss]
+The MASK can consists of 1 to 4 template characters. Each character maps to a version +part.The first character maps to the major, the second to the minor, the third to the +micro, and the last to the qualifier. These characters can be:
+= – Keep the version part- – Decrement the version part+ – Increment the version part[0-9] – Replace the version part~ – Ignore the version partS – If the qualifier equals SNAPSHOT or ends with -SNAPSHOT, then the qualifier
+will be ignored and the resulting version will be suffixed with -SNAPSHOT. This
+results in a maven version which is not a valid OSGi version. Otherwise, the existing
+qualifier, if any, will be kept. For example, ${versionmask;===S;1.2.3.SNAPSHOT} will
+return 1.2.3-SNAPSHOT and ${versionmask;===S;1.2.3.QUAL} will return 1.2.3.QUAL. s – If the qualifier equals SNAPSHOT or ends with -SNAPSHOT, then the qualifier
+will be ignored and the resulting version will be suffixed with -SNAPSHOT. This
+results in a maven version which is not a valid OSGi version. Otherwise, the existing
+qualifier, if any, will be ignored. For example, ${versionmask;===s;1.2.3.SNAPSHOT}
+will return 1.2.3-SNAPSHOT and ${versionmask;===s;1.2.3.QUAL} will return 1.2.3. In many places in bnd, the ${@} macro is set as an implicit variable. For example, if
+the bnd analyzer calculates the import range it sets this to the version of the package
+that is imported. Therefore, if the version is not specified in this macro as the second
+argument then the ${@} macro is expanded and used as the value for the version. A use
+of this is in the Import Package statement. There you can use the ${versionmask} macro
+to clean up the imported version. (Though the range macro is better suited for
+this.)
Import-Package com.exmaple.foo;version=${versionmask;==}
+This macro is also available under the name version. The name was changed because this
+often collided with macros that were defined by the user.
The vmax macro compares version strings using OSGi semantic versioning rules and returns the maximum (highest) version.
${vmax;<list>[;<list>...]}
+list - One or more semicolon-separated lists of version stringsFind maximum version:
+${vmax;1.2.3,2.0.0,1.0.5}
+# Returns: "2.0.0"
+Multiple lists:
+${vmax;1.5.0,2.0.0;1.2.3,3.0.0}
+# Returns: "3.0.0"
+Get latest version:
+latest.version=${vmax;${available.versions}}
+${vmin} for minimum version${vcompare} for comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/vmin.html b/org.bndtools.help/docs/macros/vmin.html new file mode 100644 index 0000000000..9693d334cd --- /dev/null +++ b/org.bndtools.help/docs/macros/vmin.html @@ -0,0 +1,61 @@ + + + + +The vmin macro compares version strings using OSGi semantic versioning rules and returns the minimum (lowest) version.
${vmin;<list>[;<list>...]}
+list - One or more semicolon-separated lists of version stringsFind minimum version:
+${vmin;1.2.3,2.0.0,1.0.5}
+# Returns: "1.0.5"
+Multiple lists:
+${vmin;1.5.0,2.0.0;1.2.3,3.0.0}
+# Returns: "1.2.3"
+Check compatibility:
+min.required=${vmin;${bundle.versions}}
+${vmax} for maximum version${vcompare} for comparisonSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/warning.html b/org.bndtools.help/docs/macros/warning.html new file mode 100644 index 0000000000..92dcc92fc6 --- /dev/null +++ b/org.bndtools.help/docs/macros/warning.html @@ -0,0 +1,70 @@ + + + + +The warning macro generates one or more build warnings with custom messages. Each message argument is processed (macros are expanded) and then added to the build warning list. Unlike errors, warnings do not fail the build.
${warning;<message>[;<message>...]}
+message - One or more warning messages to generate. Each message can contain macros that will be expanded.${error})Generate a simple warning:
+${warning;This configuration is deprecated}
+Generate warning with variable substitution:
+${warning;Using default version: ${version}}
+Conditional warning:
+${if;${is;${someproperty}};${warning;Property someproperty is set and may cause issues}}
+Multiple warning messages:
+${warning;First warning;Second warning;Third warning}
+Warning with computed values:
+${warning;Bundle ${bsn} is using experimental features}
+${error} to fail the build insteadSee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/macros/workspace.html b/org.bndtools.help/docs/macros/workspace.html new file mode 100644 index 0000000000..0928a4d8eb --- /dev/null +++ b/org.bndtools.help/docs/macros/workspace.html @@ -0,0 +1,55 @@ + + + + +The workspace macro returns the absolute file path to the current bnd workspace directory.
${workspace}
+None - this macro takes no parameters.
+cnf/Get workspace path:
+Workspace-Location: ${workspace}
+Reference workspace files:
+config.file=${workspace}/cnf/build.bnd
+Relative to workspace:
+${if;${startswith;${basedir};${workspace}};in-workspace;external}
+cnf/)${basedir} for project directory${propertiesdir} for config file directorySee test cases in MacroTestsForDocsExamples.java
+ + diff --git a/org.bndtools.help/docs/manual/about.html b/org.bndtools.help/docs/manual/about.html new file mode 100644 index 0000000000..fe5a1abde7 --- /dev/null +++ b/org.bndtools.help/docs/manual/about.html @@ -0,0 +1,18 @@ + + + + +Bndtools is an Eclipse Plugin based on bnd with the goal to make programming for OSGi easier than in +any other environment. Correction, to make programming in Java easier than any other environment!
+As a plugin Bndtools hooks into the IDE in numerous places. Clearly, Eclipse already provides a very comprehensive +Java programming environment and Bndtools leverages this to the hilt.
+The following image provides an overview of the most important views in the Bndtools plugin. You can click on the +view and it should take you to the appropriate place.
+
The bnd editor provides a number of tabs with graphic editors. The following tabs are available:
+





This manual describes the Bndtools Eclipse plugin user interface components.
+Full documentation available at https://bndtools.org/manual/
+ + diff --git a/org.bndtools.help/docs/manual/jareditor.html b/org.bndtools.help/docs/manual/jareditor.html new file mode 100644 index 0000000000..3ac4ca3e2e --- /dev/null +++ b/org.bndtools.help/docs/manual/jareditor.html @@ -0,0 +1,32 @@ + + + + +Double clicking a JAR or ZIP file wil open the Bndtools JAR Editor. It will display the JAR file's content with the following tabs:
+ +If the JAR file changes on the file system, it will be automatically updated.
+In the content tab you can double click entries, this will open a new editor on that file. You can also drag files from the list on the file system or the Eclipse lists that accepts files.
+
You can view the files as text or as binary. The JAR Editor guess what the preferred view is. In the text display, a special character set can be selected.
+

In the print page, if you use cmd/ctrl+f, this will open a "find..." text box in the header. Once the "find" text box is open, you can close it with the "close" button on right hand side.

Once you type in some search text, it will highlight all of the matches in the whole page, and for the first occurrence it will set the selection on it as well as modify the highlight to signal its the "current" highlight.
+
You can advance to next and previous match using arrows and the match/totalCount is displayed. Also you can use the global "next" and "previous" shortcuts as well. cmd/ctrl+., shift+cmd/ctrl+. respectively.

If the next occurrence is out of the current viewport of the text area, it will scroll the text view to show the new highlight.
+ + diff --git a/org.bndtools.help/docs/manual/packageexplorer.html b/org.bndtools.help/docs/manual/packageexplorer.html new file mode 100644 index 0000000000..d4fa9a0e77 --- /dev/null +++ b/org.bndtools.help/docs/manual/packageexplorer.html @@ -0,0 +1,101 @@ + + + + +The Bndtools Package Explorer is an extension of the Eclipse Package Explorer. The explorer provides an overview of the projects and their contents. It is extended with a search bar for projects and some extra filters. You can make simple searches or use the bnd globbing. That is, you can use wildcards (example*bar) but also multiple searches in one, for example, foo|bar to find multiple projects.
Icons are used extensively to mark the projects in the explorer. They are either used as direct icons for an object (e.g. a bnd.bnd file) or are used to decorate existing icons. For example, if a package is exported or not. The following image shows all icons in their context.
+
You can filter on the following additional targets:
+:error – Will select a project that has errors:warning – Will select a project that has warnings
Note that you can combine the pseudo targets with the '|' operator, e.g. :e|lib.
The All Green field shows the aggregate status of the workspace. If there is any project with an error, it will be red. If there are no errors, but a project has warnings it will be orange. In all other cases it is green.
+The refresh button will refresh all projects from the workspace and will then reload the workspace. This will reinitialize all the repositories and tend to rebuild all projects.
+You can pin a project. A pinned project will always be visible.
+Bndtools adds the following icons in the Package Explorer:
+| Icon | +Description | +
|---|---|
| bnd-project | +Contains all the project configuration. The project configuration inherits properties from cnf/build.bnd and any bnd files in cnf/ext/ |
+
| *.bnd file | +A general bnd file. This is either a bnd file in the cnf directory to configure the workspace or it can be a sub bundle when in a project. Bndtools contains a special editor for the bnd files. This type of file is generally used in an inheritance hierarchy. |
+
| *.bndrun file | +Describes a launch configuration. The required bundles can be indicated with initial requirements that the Bndtools resolver can turn into a list of bundles. An extensive editor is available to edit the launch configuration. A project can contain many bndrun files. | +
| build.bnd | +This is the master configuration file of the workspace. When Bndtools starts, bnd first reads the bnd files in the cnf/ext/ directory, and then the cnf/build.bnd file. Any property or header set in this file is available to any project. There is only one such file in a workspace and it is also used as the marker file for a workspace. |
+
| Bnd Bundle Path | +Bnd Bundle Path – This is an Eclipse Library container for Bndtools. The children of the container are specified in the project's bnd.bnd file in the -buildpath and -testpath instructions. |
+
| Dependency with source | +A Bnd Bundle Path dependency with source code attached. If the icon is darker then it is a test dependency. |
+
| Dependency | +A Bnd Bundle Path dependency without source code attached. If the icon is darker then it is a test dependency. |
+
| Test Dependency | +A Bnd Bundle Path dependency without source code attached that is only used for test code. |
+
| Component class | +A class that is a DS component. The general icon for a component is an eco friendly green LED. | +
| Component package | +A package that contains DS components | +
| src folder | +The source folder classes that are part of the bundle | +
| test folder | +The source folder for test code. | +
| Private package | +A private package in a bundle. This package is included but not exported. | +
| Exported package | +An exported package of a bundle. If the package has a version it will also be displayed. | +
| Excluded package | +A package that is not included in any bundle. This is rare and not a good practice so it is generally a warning sign. | +
If you're looking for other icons, the Eclipse Help shows a further reference of all icons.
+ + diff --git a/org.bndtools.help/docs/manual/repositories-view.html b/org.bndtools.help/docs/manual/repositories-view.html new file mode 100644 index 0000000000..81e4279964 --- /dev/null +++ b/org.bndtools.help/docs/manual/repositories-view.html @@ -0,0 +1,59 @@ + + + + +
Bndtools uses repositories to supply dependencies to be used at build-time and at runtime.
+A repository is essentially a collection of bundles, optionally with some kind of index.
+It may be located anywhere: in the workspace, somewhere else on the local file system, or on a remote server.
+Bndtools uses repositories in the following ways:
+Repositories are implemented as bnd plug-ins, and can be configured by editing the Plugins sections of the workspace configuration file (Bndtools menu » Open main bnd config). +Repositories can be tagged to control how they are used for resolving dependencies.
+
Since repositories are implemented as plug-ins, it is theoretically possible to support almost any kind of repository, by developing a new plug-in type; though of course it is more convenient to use an existing repository plug-in.
+The special repo bnd-cache is a mini cache repo that is expanded in the cnf directory to cache some intermediate files.
Bndtools supports a collection of repositories based on an index file that reports the content of the repository along with the capabilities and requirements of each resource listed. There are multiple available formats for the index:
+The advantage of using indexed repositories is that they can be used for automatic Resolution in the bndrun editor.
+This repository is one of "indexed" repositories, therefore index property points to central.maven file that contains GAV coordinates to libraries located in Maven Central.
central.maven file doesn't fill itself automatically. Think of this repository like an empty <dependencies> section in maven. In order to fill it you need to add GAVs (one per line) to it.
Example:
+# List repository contents using GAV coordinates
+org.apache.camel:camel-core:2.23.1
+The OSGiRepository can use index files compatible to the OSGi Repository Service Specification. A OSGiRepository cannot be modified from within bnd or Bndtools.
+ +This repository maintains a local filesystem directory of bundles. The repository is editable from within bnd/Bndtools and the index file is regenerated automatically when bundles are deployed into it.
+ +This type of repository is based on a very simple file system directory structure.
+ + + diff --git a/org.bndtools.help/docs/manual/resolution-view.html b/org.bndtools.help/docs/manual/resolution-view.html new file mode 100644 index 0000000000..e6f7e9f301 --- /dev/null +++ b/org.bndtools.help/docs/manual/resolution-view.html @@ -0,0 +1,33 @@ + + + + +
The Resolution View is a powerful tool to analyse the requirements and capabilities of bundles and their dependencies. This shows the requirements and capabilities side by side of one or multiple selected resources such as JAR file(s), a bnd.bnd file or entries in the Repository Browser (bundles in a repository or repositories).
Especially when it comes to resolution issues (e.g. required package cannot be satisfied by another bundle) this is the tool to make resolution issues visible.
+By selecting a bundle A and a repository R in the Repository View, it allows you to see the aggregated Requirements and Capabilities of both. In other words it shows you:
+If there is a requirement for which no other bundle provides a capability, then the Resolve would fail - and you can make that visible in the resolution view.
+This makes it a valuable tool used after a failed resolution (see the Resolve button in the .bnd / .bndrun editor) and its (sometimes cryptic) error message.
+Ctrl / Cmd + C for Requirements and Capabilities which copies their tooltip contents. This helps during debugging and communicating with others about it.Background information can also be found in the manual at Resolving Dependencies.
+ + diff --git a/org.bndtools.help/docs/manual/templates-osgi-service.html b/org.bndtools.help/docs/manual/templates-osgi-service.html new file mode 100644 index 0000000000..ada62bd8b5 --- /dev/null +++ b/org.bndtools.help/docs/manual/templates-osgi-service.html @@ -0,0 +1,44 @@ + + + + +This template is based on the idea of this tutorial and will create three bundles for a typical OSGi service scenario:
+@Component) of the service interface@ReferenceRequirements:
+



There are three projects created: org.myorg.test.hello (api), org.myorg.test.hello.impl, and org.myorg.test.hello.consumer.
+Included in the generated org.myorg.test.hello project is a Hello.bndrun file that can be used to launch a framework, register the HelloServiceImpl, inject (via SCR) into the HelloServiceConsumer and have the HelloServiceConsumer call the service.

Click Resolve and then Update to let bndtools figure out the bundles to run (-runbundles).


Then click on Run OSGi (or Debug OSGi). Results in the service method being called and the console output.
+Now you have a good starting point to develop your service. +In an existing project you probably do not need the generated consumer bundle if you already have other bundles consuming the service.
+ + diff --git a/org.bndtools.help/docs/manual/templates.html b/org.bndtools.help/docs/manual/templates.html new file mode 100644 index 0000000000..56b528a2a3 --- /dev/null +++ b/org.bndtools.help/docs/manual/templates.html @@ -0,0 +1,15 @@ + + + + +The Bndtools supports wizard-based creation of various elements required for OSGi development. There are templates for Bnd-Workspaces, Projects, Components, .bnd and .bndrun files and even for creating services consisting of multiple bundles.
+