Skip to content

Commit 8a25f6d

Browse files
committed
add and rework enRoute JAR Wrapping Tutorial
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
1 parent 6004506 commit 8a25f6d

38 files changed

+1153
-0
lines changed

_config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ collections:
4040
output: true
4141
tutorial_maven:
4242
output: true
43+
tutorial_wrap:
44+
output: true
4345
videos:
4446
output: true
4547

_tutorial_wrap/050-start.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
title: JAR Wrapping Tutorial
3+
layout: prev-next-collection
4+
lnext: /tutorial_wrap/100-shouldyou
5+
lprev: /book/150-tutorials
6+
noindex: true
7+
---
8+
9+
This tutorial is under review. Feedback appreciated (PRs on [Github](https://github.com/bndtools/bndtools.github.io/tree/master/_tutorial_wrap))
10+
{: .note }
11+
12+
## Why to wrap JAR files?
13+
14+
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 tutorial details an approach to OSGi bundle production using bnd/bndtools/gradle.
15+
16+
## What you learn in this tutorial.
17+
18+
In this quick start we learn how to _wrap_ a JAR to become a Bundle. Wrapping a JAR means that we need add the required OSGi manifest headers but also _design_ the contents of the bundle. Modularity is not about fences, modularity is about what you put inside those fences and what passages you allow. The bnd tool provides an overwhelming amount of instructions and features to create the Bundle you want; this tutorial tries to shine light on what forces are in play and what tools are available.
19+
20+
This tutorial teaches the wrapping from the perspective of a Bndtools user. For any command line zealots this should not be too hard to map to `vi` since all we do is write a `bnd.bnd` file in Bndtools, which is also usable in for example Maven. The key advantage of Bndtools is that it shows you the missing packages interactively. If you want to stay on the command line, then you could take a look at [bnd Wrapping](https://bnd.bndtools.org/chapters/390-wrapping.html).
21+
22+
In the coming chapters it is assumed you have a workspace ready. If you've no clue what we're talking about suggest you follow the [Quick Start] tutorial first.
23+
24+
**A disclaimer.** This wrapping tutorial is about learning to use wrapping bundles inside the OSGi enRoute tool chain, it is not about learning Java, Git, nor Eclipse. It is assumed that you have basic experience with these tools and that you have at least followed the [Quick Start] tutorial.
25+
26+
If you're just interested in the end result, you can look at the (archived) [osgi.enroute.examples.wrapping.dom4j.adapter] project.
27+
28+
If you have any questions about this wrapping tutorial, please discuss them in the [Forum].
29+
30+
## Sections
31+
32+
<div>
33+
<table>
34+
<colgroup>
35+
<col style="width:50%">
36+
<col style="width:50%">
37+
</colgroup>
38+
<tbody>
39+
{% for qs in site.tutorial_wrap %}{%unless qs.noindex%}<tr><td><a href="{{qs.url}}">{{qs.title}}</a></td><td>{{qs.summary}}</td></tr>
40+
{%endunless%}{% endfor %}
41+
</tbody>
42+
</table>
43+
</div>
44+
45+
46+
## End
47+
48+
So, you've finished this wrapping tutorial! What's next?
49+
50+
Well, first, since we're still in beta, we'd love feedback. Our most favorite feedback is a pull request on the documentation. We, and others like you, highly appreciate these kind of contributions.
51+
52+
If you've become interested in what bnd can do for you, then you could look at the [wrapping with bnd] chapter in the bnd manual.
53+
54+
However, running into real problems is the best way to learn a technology. If you run into problems, use the [Forum] to ask questions and get answers.
55+
56+
[forum]: https://bnd.discourse.group/
57+
[Quick Start]: /qs/050-start
58+
[wrapping with bnd]: https://bnd.bndtools.org/chapters/390-wrapping.html
59+
[-conditionalpackage]: https://bnd.bndtools.org/instructions/conditionalpackage.html
60+
[133 Service Loader Mediator Specification]: https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
61+
[semanticaly versioned]: https://bnd.bndtools.org/chapters/170-versioning.html
62+
[135.3 osgi.contract Namespace]: https://blog.osgi.org/2013/08/osgi-contracts-wonkish.html
63+
[BSD style license]: https://dom4j.sourceforge.net/dom4j-1.6.1/license.html
64+
[supernodes of small worlds]: https://en.wikipedia.org/wiki/Small-world_network
65+
[OSGiSemVer]: https://www.osgi.org/wp-content/uploads/SemanticVersioning.pdf
66+
[osgi.enroute.examples.wrapping.dom4j.adapter]: https://github.com/osgi/osgi.enroute.examples/tree/485624f6cb66df91f668d6eb9a5c8e491312c8c4/osgi.enroute.examples.wrapping.dom4j.adapter

_tutorial_wrap/100-shouldyou.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Should You?
3+
summary: Consider alternatives to wrapping a bundle yourself, realize you do incur a long term technical debt.
4+
layout: prev-next-collection
5+
---
6+
7+
Before we continue we must raise the question: "Do you really need that dependency?" In the software world we have an abundance of open source projects that provide us with almost any desired functionality today. The fact that a JAR is not _OSGified_ is actually a warning sign because adding the minimal OSGi headers requires very little work. Almost all successful open source libraries nowadays include OSGi support out of the box.
8+
9+
The second warning sign you should be aware off is the sheer number of dependencies. If your target JAR has > ±10 external dependencies then you should seriously consider skipping it if it is anyway possible. Though it is okay for applications to have this many dependencies, it is _extremely_ painful to have a component with such a large fan out. Not only are such dependencies tedious, over time they tend to become an unsolvable problem because the dependencies of this component start to constraint your own dependencies.
10+
11+
A good component is highly _cohesive_ and has minimal (preferably API based) dependencies.
12+
13+
The third thing to consider is that OSGi is not about class loading; it is about _services_. The highest value in OSGi lies in its service model. With the service model it is possible to keep virtually all implementation details hidden inside the bundle. Unfortunately, writing service oriented bundles is an architectural aspect; it is hard to add this in general to a JAR that is written to reside on the class path and thrive on class loader hacks.
14+
15+
## Which bundle has the package?
16+
17+
And we hardly dare to raise the issue, did someone already provide the wrapped bundle for you?
18+
19+
This is still difficult. Your best chances are:
20+
21+
- Search for the package in a Search engine like Google
22+
- or ask one of the AI Tools like ChatGPT, Perplexity, Deepseek etc.
23+
24+
The find the dependeny on Maven Central, copy the GAV coordinates and add them to your repository in bnd / bndtools.
25+
26+
There are also some resources on the net that provide wrapped bundles:
27+
28+
* [Eclipse Orbit](http://www.eclipse.org/orbit) – An Eclipse repository with OSGi bundles derived from open source projects.
29+
* Legacy: [Amdatu](https://repository.amdatu.org/) – Amdatu maintains a set of dependencies that are bundles
30+
31+
Ok, you're still here ... So I guess the need is dire. Let's see how we can wrap an existing bundle.
32+
33+
[-conditionalpackage]: http://bnd.bndtools.org/instructions/conditionalpackage.html
34+
[133 Service Loader Mediator Specification]: http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
35+
[semanticaly versioned]: http://bnd.bndtools.org/chapters/170-versioning.html
36+
[135.3 osgi.contract Namespace]: http://blog.osgi.org/2013/08/osgi-contracts-wonkish.html
37+
[BSD style license]: http://dom4j.sourceforge.net/dom4j-1.6.1/license.html
38+
[supernodes of small worlds]: https://en.wikipedia.org/wiki/Small-world_network
39+
[OSGiSemVer]: https://www.osgi.org/wp-content/uploads/SemanticVersioning.pdf
40+
[osgi.enroute.examples.wrapping.dom4j.adapter]: https://github.com/osgi/osgi.enroute.examples/tree/485624f6cb66df91f668d6eb9a5c8e491312c8c4/osgi.enroute.examples.wrapping.dom4j.adapter

_tutorial_wrap/150-strategy.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: Strategy
3+
summary: How do we go about wrapping a bundle? What are the forces?
4+
layout: prev-next-collection
5+
---
6+
7+
Though you can simply add the manifest headers to a JAR and make it an official bundle it is recommended to take a look at its dependencies. Good bundles are _cohesive_. That is, they perform one well defined function and make that available through a minimal API.
8+
9+
Their dependencies are minimal and are of the _abstraction_ type. That is, a good bundle should not depend on a library like Guava because it was convenient to use a very small set of types from this huge library. A dependency should be a point where the outer application can provide a specific implementation that fits its requirements. Due to the abstraction points, a bundle can then live in many different contexts.
10+
11+
The strategy for good bundles is therefore to hide any implementation dependencies and only import and export API packages. Obviously, this is the goal and not always achievable. It is especially hard for JARs that are not modular, the majority.
12+
13+
A simple strategy would be to export all packages (`Export-Package: *`) and let *bnd* do the work.
14+
15+
Unfortunately when wrapping third-party libraries it sometimes is 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.
16+
17+
**For example:**
18+
19+
- Classes that implement *optional* features are sometimes placed into a
20+
library’s "core" JAR. For example the Log4J library includes
21+
optional "appenders" for writing log messages to emails, JMS queues
22+
and JMX/JDMK. As a result it depends on inter alia the javax.jms
23+
package, and we have to include the JMS API bundle in order for
24+
logging to work at all!
25+
- In other cases a library may contain "dead code" — i.e. code that is
26+
not reachable from the public API — and that code may have external
27+
dependencies.
28+
29+
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.
30+
31+
In this tutorial we will follow the strategy of exporting all the packages but then carefully craft the resulting bundle based on the different problems one typically encounters wrapping bundles.
32+
33+
That said, the actual wrapping is contrived, it is not an actual industrial ready wrapping.
34+
35+
[-conditionalpackage]: http://bnd.bndtools.org/instructions/conditionalpackage.html
36+
[blog]: http://njbartlett.name/2014/05/26/static-linking.html
37+
[133 Service Loader Mediator Specification]: http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
38+
[semanticaly versioned]: http://bnd.bndtools.org/chapters/170-versioning.html
39+
[135.3 osgi.contract Namespace]: http://blog.osgi.org/2013/08/osgi-contracts-wonkish.html
40+
[BSD style license]: http://dom4j.sourceforge.net/dom4j-1.6.1/license.html
41+
[supernodes of small worlds]: https://en.wikipedia.org/wiki/Small-world_network
42+
[OSGiSemVer]: https://www.osgi.org/wp-content/uploads/SemanticVersioning.pdf
43+
[osgi.enroute.examples.wrapping.dom4j.adapter]: https://github.com/osgi/osgi.enroute.examples/tree/485624f6cb66df91f668d6eb9a5c8e491312c8c4/osgi.enroute.examples.wrapping.dom4j.adapter

_tutorial_wrap/200-project.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
title: Creating the Initial Project
3+
summary: Setup the project with DOM4J and make an initial cut
4+
layout: prev-next-collection
5+
---
6+
7+
In this tutorial we will wrap [DOM4J]. This JAR is available on Maven Central. The latest versions are actually OSGi enabled but there are variations around without manifest headers. It is chose because its dependencies are not too awful. It also depends on a number of external libraries that each also have dependencies, ad nauseum.
8+
9+
> ##### WARNING
10+
>
11+
> The work we do in this tutorial **is not for use in a real bundle**. The resulting bundle is not tested an might not work. The only goal is to show the forces at play when you wrap a bundle.
12+
{: .block-warning }
13+
14+
## Dependencies of DOM4J
15+
16+
Below you see the dependency graph of revision 1.6.1. For an average Maven project this actually looks quite good. (The graph shows all Maven dependencies, this is in general a bit too wide since test and compile dependencies are not transitive.) However, if a project does not stop at the maximum number of shown dependencies (>1000) then it is generally a 'good' sign.
17+
18+
![DOM4J dependencies](img/program.png){: width="70%" }
19+
20+
On Maven Central you can see this in the [Dependencies tab](https://central.sonatype.com/artifact/dom4j/dom4j/1.6.1/dependencies).
21+
22+
After we created the project, we'll drag the top version vignette on the `bnd.bnd` build tab to add it as a dependency.
23+
24+
## Creating Project
25+
26+
In Bndtools create a project `osgi.enroute.examples.wrapping.dom4j.adapter`. It is of course fine to call it differently but make sure you call make the extension `adapter` or `provider`. This extension triggers the correct template with the OSGi enRoute templates, which you should obviously use.
27+
28+
After the creation, you can delete the source code in the `src` and `test` source folders; we won't use it. (Add a `.gitignore` file in the `src` folder so that it is not ignored as an empty directory by Git.)
29+
30+
## Adding DOM4J
31+
32+
First, add the Maven GAV coordinates, i.e. `dom4j:dom4j:1.6.1` to your repository in bndtools (e.g. [MavenBndRepository](https://bnd.bndtools.org/plugins/maven.html))
33+
34+
Then double click on the `bnd.bnd` file in your project and select the `Build` tab. Now drag the domj bundle from the Repository Browserto the `Build` tab's `Build Path` list and drop it. Alternatively you can achieve the same by using the `+` Button of the `Build Path` list.
35+
36+
Then, click on the `Source` tab. The `-buildpath` looks then like:
37+
38+
-buildpath: \
39+
dom4j:dom4j;version='1.6'
40+
41+
## Version & Description
42+
43+
In general you use the version of the JAR you're wrapping. Since we're wrapping 1.6.1 we make that our own version. It is also a good idea to add a small description of what you are doing. So you should replace the Bundle-Version and Bundle-Description headers. Adding a Bundle-Copyright and Bundle-Vendor cannot harm:
44+
45+
Bundle-Version: 1.6.1.${tstamp}
46+
Bundle-Description: Wraps DOM4J for OSGi, including the primary dependencies
47+
Bundle-Copyright: OSGi enRoute
48+
Bundle-Vendor: OSGi Alliance
49+
50+
## Build Path
51+
52+
If you save the `bnd.bnd` file then the DOM4J JAR is added to your _build path_. We can inspect the contents by
53+
looking in the Eclipse Class Path container. This is the little bookshelf with the title: `Bnd Bundle Path`. The little arrow on the left allows you to open the container to see the contents. Also open the `dom4j-1.6.1` member of this container.
54+
55+
![DOM4J dependencies](img/container.png){: width="70%" }
56+
57+
## Exporting
58+
59+
Inspecting the list of packages inside the `dom4j-1.6.1` JAR shows that all package names start with `org.dom4j`. We therefore start by exporting these packages. The easiest way to do this to stay in the `Source` tab and add the text:
60+
61+
Export-Package: org.dom4j.*
62+
63+
This will export all packages on the `-buildpath` that start with `org.dom4j`!
64+
65+
## The Bundle
66+
67+
We've actually generated the bundle now. If you look in the folder `generated` then you'll find the JAR . Opening this JAR in the JAR Editor that is included with Bndtools will show you the manifest. (The `Print` tab is a little more readable.) This manifest looks (slightly reformatted) like:
68+
69+
[MANIFEST osgi.enroute.examples.wrapping.dom4j.adapter.jar]
70+
Bnd-LastModified 1458829936934
71+
Bundle-Copyright OSGi enRoute
72+
Bundle-Description Wraps DOM4J for OSGi, including
73+
the primary dependencies
74+
Bundle-ManifestVersion 2
75+
Bundle-Name osgi.enroute.examples.wrapping.dom4j.adapter
76+
Bundle-SymbolicName osgi.enroute.examples.wrapping.dom4j.adapter
77+
Bundle-Vendor OSGi Alliance
78+
Bundle-Version 1.6.1.201603241432
79+
Created-By 1.8.0_25 (Oracle Corporation)
80+
Import-Package com.sun.msv.datatype,
81+
com.sun.msv.datatype.xsd,
82+
javax.swing.table,
83+
javax.swing.tree,
84+
javax.xml.namespace,
85+
javax.xml.parsers,
86+
javax.xml.stream,
87+
javax.xml.stream.events,
88+
javax.xml.stream.util,
89+
javax.xml.transform.sax,
90+
org.gjt.xpp,
91+
org.jaxen,org.jaxen.dom4j,
92+
org.jaxen.pattern,
93+
org.jaxen.saxpath,
94+
org.relaxng.datatype,
95+
org.w3c.dom,
96+
org.xml.sax,
97+
org.xml.sax.ext,
98+
org.xml.sax.helpers,
99+
org.xmlpull.v1
100+
Manifest-Version 1.0
101+
Private-Package org.dom4j.rule.pattern,
102+
org.dom4j.swing,
103+
org.dom4j.tree,
104+
org.dom4j.dtd,
105+
org.dom4j.util,
106+
org.dom4j.xpp,
107+
org.dom4j,
108+
org.dom4j.bean,
109+
org.dom4j.datatype,
110+
org.dom4j.rule,
111+
org.dom4j.io,
112+
org.dom4j.xpath
113+
Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.3))"
114+
Tool Bnd-3.2.0.201603172351-SNAPSHOT
115+
116+
Obviously we succeeded in including the `org.dom4j` packages but we got a few imports that are not in the JVM like `org.jaxen`, `org.gjt.xpp`, and `org.relaxng`.
117+
118+
119+
[DOM4J]: http://jpm4j.org/#!/p/org.jdom/jdom
120+
[JPM4J]: http://jpm4j.org/
121+
[-conditionalpackage]: http://bnd.bndtools.org/instructions/conditionalpackage.html
122+
[blog]: http://njbartlett.name/2014/05/26/static-linking.html
123+
[133 Service Loader Mediator Specification]: http://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
124+
[semanticaly versioned]: http://bnd.bndtools.org/chapters/170-versioning.html
125+
[135.3 osgi.contract Namespace]: http://blog.osgi.org/2013/08/osgi-contracts-wonkish.html
126+
[BSD style license]: http://dom4j.sourceforge.net/dom4j-1.6.1/license.html
127+
[supernodes of small worlds]: https://en.wikipedia.org/wiki/Small-world_network
128+
[OSGiSemVer]: https://www.osgi.org/wp-content/uploads/SemanticVersioning.pdf
129+
[osgi.enroute.examples.wrapping.dom4j.adapter]: https://github.com/osgi/osgi.enroute.examples/tree/485624f6cb66df91f668d6eb9a5c8e491312c8c4/osgi.enroute.examples.wrapping.dom4j.adapter

0 commit comments

Comments
 (0)