From 90597b4c47992c4ef273b9a4b3ee851d7f0eda9b Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Mon, 6 Jun 2022 17:14:28 -0400 Subject: [PATCH 1/3] docs: Remove old enroute references from doc text Some use of enroute appears in examples which is ok. Fixes https://github.com/bndtools/bnd/issues/5198 Signed-off-by: BJ Hargrave --- docs/_chapters/250-resolving.md | 78 ++++++++++++------------- docs/_instructions/resolve.effective.md | 4 +- docs/_layouts/front.html | 2 - docs/_plugins/pomrepo.md | 16 ++--- docs/css/style.scss | 14 ----- 5 files changed, 45 insertions(+), 69 deletions(-) diff --git a/docs/_chapters/250-resolving.md b/docs/_chapters/250-resolving.md index 93fa0312ea..0df21c0822 100644 --- a/docs/_chapters/250-resolving.md +++ b/docs/_chapters/250-resolving.md @@ -20,7 +20,7 @@ Unfortunately, the Maven dependency model has limitations for a number of reason These are the causes: * **Our small minds** – The human mind and human motivation provide little support for properly maintaining any amount of meta-data. Many open source projects show unnecessary dependencies that were once needed but no longer necessary. Fortunately, Maven Central/Sonatype has become much more restrictive. However, in the past, a lot of Maven dependencies were quite bad. -* **Aggregate problem** – The aggregate problem stems from a dependency that is used for one small part but where a co-packaged part drags in other (and therefore unnecessary) dependencies. For example, many JARs provid support for Ant, a former build tool. This was useful if you happened to use Ant but it caused the addition of the Ant runtime in systems that never used Ant. The aggregate problem causes a serious increase of dependency fan-out in many projects. +* **Aggregate problem** – The aggregate problem stems from a dependency that is used for one small part but where a co-packaged part drags in other (and therefore unnecessary) dependencies. For example, many JARs provide support for Ant, a former build tool. This was useful if you happened to use Ant but it caused the addition of the Ant runtime in systems that never used Ant. The aggregate problem causes a serious increase of dependency fan-out in many projects. * **Version Conflicts** – When you drag in a lot of dependencies you invariably run into a situation where two of your dependencies require the same third dependency but in different versions. (This is called the _diamond_ problem for obvious reasons if you draw the graph.) Sometimes these versions are really incompatible, sometimes the higher would suffice. However, in Maven, the first artefact encountered in a breadth first search defines the version. * **Variations** – In many situations it is necessary to create different variations of an application. For example, an application could be delivered for different platforms or in a demo variation and a full monty variation. Since the Maven transitive dependency model creates a fixed graph it is not suitable to assemble these variations easily. That is, there often is not a single runtime. @@ -35,7 +35,7 @@ Establishing the perfect class path for an application is a process for which th The design of the resolving model is quite simple. It consists of the following entities: * **Resource** – A resource is a _description_ of an artefact that can be _installed_ in a system. When it is installed, it adds functions to that system. However, before it can be installed it requires certain functions to already be present. A resource can describe a bundle but it could also describe a piece of hardware or a certificate. It is important to realise that a resource is _not_ the artefact, it is a description of the _relation_ between the artefact and the target system. For example, in OSGi the bundle is a JAR file that can be installed in a framework, this is the artefact. A resource describes formally what that bundle can contribute to the system and what it needs from the system. -* **Capability** – A capability is a description of a resource's contribution to the system when its artefact is installed. A capability has a _type_ and a set of _properties_. That is, it is a bit like a DTO (or _struct_). The type is defined by name and is therefore called the _namespace_. The properties are key value pairs, where the keys are strings and values can be scalars or collections of `String`, `Integer`, `Double`, and `Long`. +* **Capability** – A capability is a description of a resource's contribution to the system when its artefact is installed. A capability has a _type_ and a set of _properties_. That is, it is a bit like a DTO (or _struct_). The type is defined by name and is therefore called the _namespace_. The properties are key value pairs, where the keys are strings and values can be scalars or collections of `String`, `Integer`, `Double`, and `Long`. * **Requirement** – A requirement represents the _needs_ of an artefact when it is installed in the system. Since we describe the system with capabilities, we need a way to assert the properties of a capability in a given namespace. A requirement therefore consists of a type and an _OSGi filter_. The type is the same as for the capabilities, it is the namespace. The filter is constructed from a powerful filter language based on LDAP. A filter can assert expressions of arbitrary complexity. A requirement can be _mandatory_ or _optional_. * **Namespace** – Since a requirement can match any set of properties it is necessary to scope the capabilities it can be asserted against. This is achieved by giving a requirement and a capability a namespace. A requirement can only match a capability when the capability has the same namespace. @@ -45,7 +45,7 @@ It is important to realise that a resource and its capabilities and requirements ### Core Namespaces -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. +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. @@ -73,36 +73,36 @@ To make the model more clear let's take a closer look to a simple bundle `com.ex ## Who Wants to Provide That Gibberish??? -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. +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](230-manifest-annotations.html) 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. +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. ## How to Write Resolvable Bundles 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. -* **Minimise Dependencies** – Every dependency has a cost. Always consider if all dependencies are _really_ necessary. Although a dependency can provide a short term gain for the coder, it will have consequences for the later stages in the development process. Perfect bundles are bundles that have no dependencies. Unfortunately they are also often useless (although you can still build a lot from Java SE `java.*` which is an implicit import). Therefore, a developer must alway be aware of this trade off. +* **Minimise Dependencies** – Every dependency has a cost. Always consider if all dependencies are _really_ necessary. Although a dependency can provide a short term gain for the coder, it will have consequences for the later stages in the development process. Perfect bundles are bundles that have no dependencies. Unfortunately they are also often useless (although you can still build a lot from Java SE `java.*` which is an implicit import). Therefore, a developer must always be aware of this trade off. * **API Driven** – Always, always, always separate API and implementation. A lot of developers avoid the overhead of developing an API when they consider the (initial) problem too simple to do the effort of a separate API. The author of this App note is one of them but he never, ever did not regret this. Usually he then spent a lot of extra effort to add an API afterwards. -* **Service Oriented** – The best dependencies are service oriented dependencies. Services make the coupling between modules explicit and allow different implementations to provide the same service. For example, in OSGi enRoute there is a simple DTOs service that handles type conversions and JSON parsing/generating. In projects where I see the explicit use of Jackson it always seems to cause a mess of required JARs visible in each bundle project. +* **Service Oriented** – The best dependencies are service oriented dependencies. Services make the coupling between modules explicit and allow different implementations to provide the same service. For example, use a simple DTOs service that handles type conversions and JSON parsing/generating. In projects where I see the explicit use of Jackson it always seems to cause a mess of required JARs visible in each bundle project. * **Package Imports** – A bundle can require other bundles (the Maven model) or it can import packages from other bundles. The best imported packages are the ones used for services since they are _specification only_. Second best are _libraries_. Library packages have no internal state nor use statics. Importing implementation packages for convenience is usually deemed bad practice because it often indicates that the decomposition in bundles is not optimal. * **Import the Exports** – If a package is exported **and** used inside the same bundle then the exported package should also be imported. This allows the resolver more leeway. -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. +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. -* ![service](https://user-images.githubusercontent.com/200494/31222248-c962b09c-a9c6-11e7-99ac-1977a6c736fd.png) – `osgi.service`. +* ![service](https://user-images.githubusercontent.com/200494/31222248-c962b09c-a9c6-11e7-99ac-1977a6c736fd.png) – `osgi.service`. * ![bullet_green](https://user-images.githubusercontent.com/200494/31222315-f8e60454-a9c6-11e7-82d8-bbaa7acc46b1.png) – `osgi.identity` * ![package](https://user-images.githubusercontent.com/200494/31222346-1153abf4-a9c7-11e7-97df-1bfa7a967505.gif) – `osgi.wiring.package` * ![java](https://user-images.githubusercontent.com/200494/31222445-6a2231ce-a9c7-11e7-8242-c226709f9a76.png) – `osgi.ee` * ![wand](https://user-images.githubusercontent.com/200494/31222450-7156a204-a9c7-11e7-800e-9c67837f4cef.png) – `osgi.extender` * ![bundle](https://user-images.githubusercontent.com/200494/31222500-9460eade-a9c7-11e7-9a75-50ae25a35c00.png) – `osgi.bundle` -Developers that are keen on develping good bundles should pay close attention to this pane. +Developers that are keen on developing good bundles should pay close attention to this pane. ## Resolving @@ -111,16 +111,16 @@ Developers that are keen on develping good bundles should pay close attention to The previous diagram adds the resolver to the earlier diagram of the basic model. It adds the following entities: * **Repository** – A Repository is a collection of resources. Repositories can be represented in many different ways as will be discussed later. -* **Resolver** – The Resolver is the entity that takes a set of _initial requirements_, _system capabilities_, a set of repositories, and produces a _resolution_. +* **Resolver** – The Resolver is the entity that takes a set of _initial requirements_, _system capabilities_, a set of repositories, and produces a _resolution_. * **Resolution** – A resolution is a _wiring_ of resources. It provides detailed information about what requirements are matched with what capabilities from the included resources. -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. +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. ## Repositories -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. +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. -The 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. +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. ### Available Repositories @@ -136,7 +136,7 @@ Since bnd has an extensive library to parse bundles and generate the resources i ### Managing Repositories -A repository generally represents a _release_ of a software product. For example, in OSGi enRoute, the OSGi Alliance has provided a _base API_ bundle. This bundle contains all the APIs of OSGi core, OSGi compendium, and an OSGi enRoute release. There is also an OSGi enRoute _distro_ for each release. The distro is a repository that provides access to implementations for the base API. The OSGi enRoute distro is maintained in a Maven POM. This POM lists the exact versions of a release. Many developers publish an OSGi Repository XML to allow the resolver access to the metadata of their bundles, for example [Knopflerfish](https://www.knopflerfish.org/releases/4.0.1/repository.xml) produces an OSGi XML Repository with all their bundles. +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](https://www.knopflerfish.org/releases/4.0.1/repository.xml) 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. @@ -146,11 +146,11 @@ A natural repository is the bnd _workspace_ since a workspace is a collection of ## Resolving in Bndtools -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. You can play along with the following explanations by creating an [OSGi enRoute Application project](/qs/050-start.html) from the templates. +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 `osgi.enroute.examples.resolver.application.bndrun` file (or whatever name you picked) it opens a _Run_ pane. +If you double click on the bndrun file, it opens a _Run_ pane. Run pane> @@ -194,15 +194,15 @@ There are some more panes that are useful but they will be handled in diagnosing 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. - +Resolution This dialog window is divided in three main parts: -* **Required Resources** – The required resources are the solution that the resolver found based on the initial requirements and the system capabilities. -* **Optional Resources** – During resolving some resources are included by an _optional_ requirement. The resolver cannot automatically add optional resources, optional resources require human choice. +* **Required Resources** – The required resources are the solution that the resolver found based on the initial requirements and the system capabilities. +* **Optional Resources** – During resolving some resources are included by an _optional_ requirement. The resolver cannot automatically add optional resources, optional resources require human choice. * **Reasons** – Clicking on a required resource or an optional resource will show a _requirements trace_ in this list. Sometimes a resource is added and it is not clear why it was added. Traversing through this list can then help finding out what caused its inclusion. If no solution was found, this list can also contain very useful information. This list is a great debugging tool. -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 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. @@ -220,9 +220,9 @@ That said, there are a number of scenarios where the resolver does give a hint w ## Missing Requirement false -This rather obscure message indicates that the resolver tries to include a _api_ bundle that was made unresolvable. +This rather obscure message indicates that the resolver tries to include a _api_ bundle that was made unresolvable. -In OSGi enRoute it is recommended to include the API package of a service in the provider bundle. Since a provider has a very tight relation to the API there is no loss of flexibility by including the package. For this reason, OSGi enRoute templates make the API bundle _compile only_. Each API has a special Require-Capability header: +The api bundle may have a special Require-Capability header such as: Require-Capability: \ compile-only @@ -232,9 +232,9 @@ This header creates a requirement that cannot be satisfied. There is nothing spe In the resolver, you will see the following error chain: Unable to resolve <> version=null: - missing requirement osgi.enroute.examples.resolver.missingapi.provider + 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) + 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]] @@ -252,11 +252,9 @@ Exporting the API package from the provider bundle will correct this case. ## Compendium Bundle -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. +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. -In OSGi enRoute the base API bundle is similar. For this reason it is also made `compile-only`. However, the compendium bundle is a perfectly valid bundle. - -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. +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. @@ -264,9 +262,9 @@ For example, we create a bundle that implements the Wire Admin service. ... } -This will cause a requirement for an exported package `org.osgi.service.wireadmin`. Since the OSGi enRoute does not provide an implementation in its distro this API is not available from the OSGi base API. We therefore add the OSGi compendium bundle to the `-buildpath`. This then compiles fine. +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. +However, the resolve will then drag in the OSGi compendium bundle. ![image](https://user-images.githubusercontent.com/200494/31179074-3153a0d2-a91b-11e7-972e-622c7db59dd5.png) @@ -274,7 +272,7 @@ If we look at the reasons when we select the compendium bundle we see that also -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. +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. ## You Are Sure There Is a Provider! @@ -319,10 +317,10 @@ The problem with metadata is that it can also be wrong. Especially legacy bundle 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. +There are two different ways to add the augments. * **Bndrun file** – Provide the augments in the `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. -* **Resource** – Provide a resource in a repository that contains an `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. See the [OSGi enRout distro](https://github.com/osgi/osgi.enroute/tree/master/osgi.enroute.pom.distro) for an example. +* **Resource** – Provide a resource in a repository that contains an `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. @@ -330,7 +328,7 @@ 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`. +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. @@ -356,15 +354,13 @@ The `capability:` and `requirement:` directives follow all the rules of the Prov 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. -A good example of a curated repository is the [OSGi enRoute Distro](https://github.com/osgi/osgi.enroute/blob/master/osgi.enroute.pom.distro/augments.bndrun). If you're responsible for a repository it might help to take a good look at the `augments.bndrun` file. - 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. ## Resolving against existing container -The enroute model tends to guide you to resolve an executable. Meanwhile 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. +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. +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. ### How do you create a distro? @@ -373,7 +369,7 @@ Currently the way to do that is to create a __distro__ of the target container. bnd remote distro -o container-5.6.7.jar container 5.6.7 -3. Take the jar `container-5.6.7.jar` created in 2. and place it into the the directory containing the bndrun file that is used to resolve your deployment jars. +3. 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. 4. in the bndrun file add: -distro: ${.}/container-5.6.7.jar;version=file @@ -387,7 +383,7 @@ What you need to bear in mind is that the __distro__ needs to be re-created each ## Conclusion -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. +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. diff --git a/docs/_instructions/resolve.effective.md b/docs/_instructions/resolve.effective.md index e1ccc87608..f3979577ad 100644 --- a/docs/_instructions/resolve.effective.md +++ b/docs/_instructions/resolve.effective.md @@ -5,7 +5,7 @@ title: -resolve.effective qname (',' qname ) summary: Set the use effectives for the resolver --- -Each requirement and capability has an `effective` or is `effective=resolve`. An effective of `resolve` is always processed by the resolver. In OSGi enRoute, also any effective of `active` is processed since this is the mode that is compatible with bnd. However, in (very) special cases it is necessary to provide more rules. +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: @@ -17,7 +17,7 @@ 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 (which is the default in OSGi enRoute). +In this case, the resolver will only look at requirements that are either resolve or active. Adding a `meta` effective could then be: diff --git a/docs/_layouts/front.html b/docs/_layouts/front.html index 2aae4e1b56..10a73ea922 100644 --- a/docs/_layouts/front.html +++ b/docs/_layouts/front.html @@ -53,8 +53,6 @@

How to get started?

If you insist on using Maven, then you can start with the description of the Maven plugins. - You can also go to the OSGi enRoute site; OSGi enRoute - is fully based on these Maven plugins.

There is also a Gradle plugin that is the preferred way to build OSGi bundles with plain old vanilla Gradle. This is described in the Gradle Plugins diff --git a/docs/_plugins/pomrepo.md b/docs/_plugins/pomrepo.md index dd8a166be8..9a4ef3bfa4 100644 --- a/docs/_plugins/pomrepo.md +++ b/docs/_plugins/pomrepo.md @@ -1,20 +1,16 @@ --- title: Bnd Pom Repository layout: default -summary: A plugin to use a Maven POM as a repository +summary: A plugin to use a Maven POM as a repository --- -A Maven POM can be viewed as the root node in an artifact transitive dependency graph. The Bnd Pom Repository plugin reads this graph and provides the set of artifacts as a bnd repository. The purpose of this plugin is to be able to have a single dependency definition that can be used by Maven projects and bnd projects. +A Maven POM can be viewed as the root node in an artifact transitive dependency graph. The Bnd Pom Repository plugin reads this graph and provides the set of artifacts as a bnd repository. The purpose of this plugin is to be able to have a single dependency definition that can be used by Maven projects and bnd projects. -The pom can be a file on the local file system, a URL, a group, artifact, version (GAV) coordinate, or a query expression on maven central. - -## Use Cases - -The use case that triggered the development of the Bnd Pom Repository is OSGi enRoute. The artifacts in the OSGi enRoute effort needed to be shared between the Bndtools workspace and the Maven examples. Expressing the dependencies in a POM allows this. In the OSGi enRoute case the distro (the implementations for the OSGi enRoute API) are maintained in a [POM](https://github.com/osgi/osgi.enroute/blob/next/osgi.enroute.pom.distro/pom.xml). This POM is used to provide the compile time dependency (the OSGi enRoute Base API jar) as well as the runtime dependencies. +The pom can be a file on the local file system, a URL, a group, artifact, version (GAV) coordinate, or a query expression on maven central. ## Example -In OSGi enRoute we need the following dependencies in a `pom.xml` file: +One can express dependencies in a `pom.xml` file: @@ -101,11 +97,11 @@ Notice that the id must match the scheme, the host, and the port if not the defa ## IDEs -The repository view in the IDE will show detailed information when you hover the mouse over the the repository entry, the program entry, or the revision entry. +The repository view in the IDE will show detailed information when you hover the mouse over the the repository entry, the program entry, or the revision entry. ## Caveats -* The repository does not use any Maven code to parse the POMs to keep the dependencies to bnd low. It attempts to strictly follow the rules prescribed for POMs concerning properties, inheritance, and ordering. However, there are a number of issues with this approach. +* The repository does not use any Maven code to parse the POMs to keep the dependencies to bnd low. It attempts to strictly follow the rules prescribed for POMs concerning properties, inheritance, and ordering. However, there are a number of issues with this approach. * bnd attempts to follow the rules, maven sometimes relaxes its own and other specification rules so you could run into errors where POMs are wrong but still accepted by Maven. * It is possible to add dependencies via a plugin. This is not supported for what should be obvious reasons. * The parser ignores repositories in POMs and restricts the repositories to the ones listes in the `releaseUrl` and `snapshotUrl` configuration parameters. This is for security reasons. diff --git a/docs/css/style.scss b/docs/css/style.scss index dd259b8aa0..28fb4f58ca 100644 --- a/docs/css/style.scss +++ b/docs/css/style.scss @@ -121,20 +121,6 @@ $color-vignette: #F0F0F0; @include vignette(); } -.enroute { - @include vignette(); - margin-top: $block-separation; - img { - width: 4em; - float: left; - vertical-align: middle; - margin-right: $text-separation; - } - &::after { - float:clear; - } -} - .selected { &:before { content: "\25B7\00A0"; } font-weight: bolder; From beff2ead84fe4c10d5c6055f0dd40974e21a002e Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Mon, 6 Jun 2022 17:15:55 -0400 Subject: [PATCH 2/3] bnd: Remove old enroute command from cli Signed-off-by: BJ Hargrave --- biz.aQute.bnd/bnd.bnd | 4 +- biz.aQute.bnd/enroute | 5 - .../bnd/enroute/commands/EnrouteCommand.java | 199 ------------------ .../bnd/enroute/commands/EnrouteOptions.java | 7 - biz.aQute.bnd/src/aQute/bnd/main/bnd.java | 11 - biz.aQute.bnd/templates/enroute.zip | Bin 109384 -> 0 bytes biz.aQute.bnd/testruns/newindex.bndrun | 7 - docs/_commands/enroute.md | 64 ------ 8 files changed, 1 insertion(+), 296 deletions(-) delete mode 100755 biz.aQute.bnd/enroute delete mode 100644 biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteCommand.java delete mode 100644 biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteOptions.java delete mode 100644 biz.aQute.bnd/templates/enroute.zip delete mode 100644 biz.aQute.bnd/testruns/newindex.bndrun delete mode 100644 docs/_commands/enroute.md diff --git a/biz.aQute.bnd/bnd.bnd b/biz.aQute.bnd/bnd.bnd index dfca7e148d..c39146d749 100644 --- a/biz.aQute.bnd/bnd.bnd +++ b/biz.aQute.bnd/bnd.bnd @@ -21,7 +21,6 @@ -includeresource: \ ${workspace}/LICENSE, \ - templates=templates, \ @${repo;biz.aQute.bnd.util;latest}!/!META-INF/*, \ @${repo;biz.aQute.bndlib;latest}!/!META-INF/*, \ @${repo;biz.aQute.bnd.ant;latest}!/!META-INF/*, \ @@ -46,7 +45,6 @@ Import-Package: \ # by hand even though they are in bndlib. However, they are added # and we can unfortunately not see those packages ... -privatepackage: \ - aQute.bnd.enroute.commands,\ aQute.bnd.main, \ org.objectweb.asm.*,\ org.jtwig.*,\ @@ -137,7 +135,7 @@ Bundle-Description: This command line utility is the Swiss army knife of OSGi. I com.github.javaparser.javaparser-core;version=3.13 --builderignore: testdata, testruns, installers +-builderignore: testdata, installers # Don't run tests in parallel with other projects claiming launchpad -noparallel: launchpad;task="test" diff --git a/biz.aQute.bnd/enroute b/biz.aQute.bnd/enroute deleted file mode 100755 index a369fdc5be..0000000000 --- a/biz.aQute.bnd/enroute +++ /dev/null @@ -1,5 +0,0 @@ -#/bin/sh -f=`pwd`/templates/enroute.zip -rm $f -cd ../../enroute/osgi.enroute.template; zip -r $f . -x ".git/*" - diff --git a/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteCommand.java b/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteCommand.java deleted file mode 100644 index 7422004b1e..0000000000 --- a/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteCommand.java +++ /dev/null @@ -1,199 +0,0 @@ -package aQute.bnd.enroute.commands; - -import java.io.File; -import java.io.InputStream; -import java.util.Formatter; -import java.util.List; -import java.util.Map.Entry; -import java.util.regex.Pattern; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import aQute.bnd.build.Workspace; -import aQute.bnd.main.bnd; -import aQute.bnd.osgi.Jar; -import aQute.bnd.osgi.Processor; -import aQute.bnd.osgi.Resource; -import aQute.bnd.osgi.Verifier; -import aQute.lib.getopt.Arguments; -import aQute.lib.getopt.Description; -import aQute.lib.getopt.Options; -import aQute.lib.io.IO; - -@Description("OSGi enRoute Commands") -public class EnrouteCommand { - private final static Logger logger = LoggerFactory.getLogger(EnrouteCommand.class); - - private bnd bnd; - private EnrouteOptions opts; - - public EnrouteCommand(bnd bnd, EnrouteOptions opts) throws Exception { - this.bnd = bnd; - this.opts = opts; - - List args = opts._arguments(); - if (args.isEmpty()) { - // Default command - printHelp(); - } else { - // Other commands - String cmd = args.remove(0); - String help = opts._command() - .execute(this, cmd, args); - if (help != null) { - bnd.out.print(help); - } - } - } - - private void printHelp() throws Exception { - Formatter f = new Formatter(); - opts._command() - .help(f, this); - bnd.out.println(f); - f.close(); - } - - @Description("Create a workspace in the base directory (working directory or set with bnd -b ). The name of the workspace should be a " - + "Bundle Symbolic Name type (like com.example.whatever). If another type of name is " - + "necessary you can override it with --anyname. Two directories will be created. One for the bnd" - + "workspace and the other for the eclipse workspace. Having two directories makes life a lot" - + "easier when you use git, it allows you to clear the bnd workspace with 'git clean -fdx' without" - + "killing any personal Eclipse data. It also prevents you from accidentally storing this" - + "personal data in git. If you know better, use --single to let these workspaces overlap.\n" - + "The directory for the workspaces must be empty unless you specify --update or --force.%n" - + "This template will also install a gradle build system and a travis continuous integration" - + "control file.%n" + "A workspace cannot be created at the root of a file system. The general layout of the" - + "file system is%n" + "%n%n" // - + " ../wss/%n" // - + " com.acme.prime/%n" + " .metadata/%n" // - + " ....%n" + " scm/%n"// - + " cnf/%n" // - + " build.bnd%n" + " ....%n" - + " com.acme.prime.runner.api%n" // - ) - @Arguments(arg = "workspace") - public interface WorkspaceOptions extends Options { - @Description("Create a single workspace for the Eclipse workspace and the bnd workspace. This is not recommended because if you fully clean the directory you delete Eclipse metadata.") - boolean single(); - - @Description("In general a workspace should follow the rules for Bundle Symbolic%n" - + "Names. If this option is set any name (including one with file seperators) is fine.") - boolean anyname(); - - @Description("Existing files are updated when they are older than the ones in the template") - boolean update(); - - @Description("Update a workspace overwrite all existing files.") - boolean force(); - } - - public void _workspace(WorkspaceOptions opts) throws Exception { - File base = bnd.getBase(); - - String name = opts._arguments() - .get(0); - - File workspaceDir = Processor.getFile(base, name); - name = workspaceDir.getName(); - base = workspaceDir.getParentFile(); - - if (base == null) { - bnd.error("You cannot create a workspace in the root (%s). The parent of a workspace %n" - + "must be a valid directory. Recommended is to dedicate a directory to %n" - + "all (or related) workspaces.", workspaceDir); - return; - } - - if (!opts.anyname() && !Verifier.isBsn(name)) { - bnd.error("You specified a workspace name that does not follow the recommended pattern " - + "(it should be like a Bundle Symbolic name). It is a bit pedantic but it " - + "really helps hwne you get many workspaces. If you insist on this name, use the -a/--anyname option."); - return; - } - - Workspace ws = bnd.getWorkspace((File) null); - - if (ws != null && ws.isValid()) { - bnd.error( - "You are currently in a workspace already (%s) in %s. You can only create a new workspace outside an existing workspace", - ws, base); - return; - } - - File eclipseDir = workspaceDir; - IO.mkdirs(workspaceDir); - - if (!opts.single()) - workspaceDir = new File(workspaceDir, "scm"); - - IO.mkdirs(workspaceDir); - - if (!base.isDirectory()) { - bnd.error("Could not create directory for the bnd workspace %s", base); - } else if (!eclipseDir.isDirectory()) { - bnd.error("Could not create directory for the Eclipse workspace %s", eclipseDir); - } - - if (!workspaceDir.isDirectory()) { - bnd.error("Could not create the workspace directory %s", workspaceDir); - return; - } - - if (!opts.update() && !opts.force() && workspaceDir.list().length > 0) { - bnd.error("The workspace directory %s is not empty, specify -u/--update to update or -f/--force to replace", - workspaceDir); - } - - InputStream in = getClass().getResourceAsStream("/templates/enroute.zip"); - if (in == null) { - bnd.error("Cannot find template in this jar %s", "/templates/enroute.zip"); - return; - } - - Pattern glob = Pattern.compile("[^/]+|cnf/.*|\\...+/.*"); - - copy(workspaceDir, in, glob, opts.force()); - - File readme = new File(workspaceDir, "README.md"); - if (readme.isFile()) - IO.copy(readme, bnd.out); - - bnd.out.printf("%nWorkspace %s created.%n%n" // - + " Start Eclipse:%n" // - + " 1) Select the Eclipse workspace %s%n" // - + " 2) Package Explorer context menu: Import/General/Existing Projects from %s%n" + "%n" + "", // - workspaceDir.getName(), eclipseDir, workspaceDir); - - } - - private void copy(File workspaceDir, InputStream in, Pattern glob, boolean overwrite) throws Exception { - - try (Jar jar = new Jar("dot", in)) { - for (Entry e : jar.getResources() - .entrySet()) { - - String path = e.getKey(); - logger.debug("path {}", path); - - if (glob != null && !glob.matcher(path) - .matches()) - continue; - - Resource r = e.getValue(); - File dest = Processor.getFile(workspaceDir, path); - if (overwrite || !dest.isFile() || dest.lastModified() < r.lastModified() || r.lastModified() <= 0) { - - logger.debug("copy {} to {}", path, dest); - - File dp = dest.getParentFile(); - IO.mkdirs(dp); - - IO.copy(r.openInputStream(), dest); - } - } - } - } - -} diff --git a/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteOptions.java b/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteOptions.java deleted file mode 100644 index b9b58a1ee8..0000000000 --- a/biz.aQute.bnd/src/aQute/bnd/enroute/commands/EnrouteOptions.java +++ /dev/null @@ -1,7 +0,0 @@ -package aQute.bnd.enroute.commands; - -import aQute.lib.getopt.Options; - -public interface EnrouteOptions extends Options { - -} diff --git a/biz.aQute.bnd/src/aQute/bnd/main/bnd.java b/biz.aQute.bnd/src/aQute/bnd/main/bnd.java index a787c3237f..ffda71f4b6 100644 --- a/biz.aQute.bnd/src/aQute/bnd/main/bnd.java +++ b/biz.aQute.bnd/src/aQute/bnd/main/bnd.java @@ -78,8 +78,6 @@ import aQute.bnd.build.Run; import aQute.bnd.build.Workspace; import aQute.bnd.buildtool.ToolManager; -import aQute.bnd.enroute.commands.EnrouteCommand; -import aQute.bnd.enroute.commands.EnrouteOptions; import aQute.bnd.exceptions.Exceptions; import aQute.bnd.exporter.subsystem.SubsystemExporter; import aQute.bnd.header.Attrs; @@ -1876,15 +1874,6 @@ public void _repo(repoOptions opts) throws Exception { new RepoCommand(this, opts); } - /** - * Run enroute commands - */ - - @Description("OSGi enRoute commands to maintain bnd workspaces (create workspace, add project, etc)") - public void _enroute(EnrouteOptions opts) throws Exception { - new EnrouteCommand(this, opts); - } - /** * Print out a JAR */ diff --git a/biz.aQute.bnd/templates/enroute.zip b/biz.aQute.bnd/templates/enroute.zip deleted file mode 100644 index 0bf1a11822dcefb72c2444a7f7e046b40bc9a247..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109384 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