Recently I’ve been playing at pairing OSGi development with Java’s
jlink mechanism. Small, fit for purpose applications seem to be the norm these days and since OSGi has all the qualities required to achieve this it seems natural to pursue this combination.
Progress was being made until I needed to debug an issue which only manifested after
Update [Mar 19, 2019]
As it turns out all you need to do is make sure that your
jlink‘d modules include
Once included, the argument:
should work as expected.
Thanks to an industry colleague for instructing me on the correct way to do this. :) You know who you are!
Ignore everything bellow this line (leaving it for posterity only.)
My first instinct was simply too add the often used command line options to for debugging, usually something like:
The entire command line looked like this (where
executable is the root of the
jlink‘d image and the name of the custom module):
$] executable/bin/java -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -m executable
This produced the error:
Error occurred during initialization of VM Could not find agent library jdwp on the library path, with error: libjdwp.so: cannot open shared object file: No such file or directory
Next, my queries to the Google didn’t provide much in the way of concrete answers. I guess there aren’t that many people who have been in this situation to this point, which I found to be odd. I’m not really sure how to explain this. I can only think of 3 possible reasons (I’ll admit there could be many more):
- people haven’t found the need to debug their
- people haven’t used
- it’s so obvious how this should be done that documenting the process isn’t useful information
Which do you think it is?
Ultimately, I had to dust off a latent skill called reading the documentation.
As it turns out (and if you think this obvious given we’re using
jlink, then you might be right) that the
jlink‘d image prunes off not only Java modules but also related native libraries which is perfectly fine. Semi-related to this the image doesn’t contain
libjdwp (the library that enables debugging).
I’m willing to accept this as a fact that my image didn’t include
java.instrument which is the API I’d guess most likely links the functionality of
libjdwp. Sadly, even including
java.instrument doesn’t cause
libjdwp to be included with the image which kind of breaks with symmetry.
Alternatively it would be nice if there was a
jlink switch which allowed me to include particular libs as part of the build. I personally feel
libjdwp is pretty critical in most cases.
Luckily help information provided by the
java -h command explains how to add a library which isn’t found in the default library path of the image:
-agentpath:<pathname>[=<options>] load native agent library by full pathname
I suspected that I should use the library that matched the version of the JDK (
11) I had used to build the
jlink image, but for fun I tried to use a different version to see what would happen (note I’m on Linux and using Azul Zulu of which I have JDK 7 through 11 installed, so mixing and matching here was simple):
$] executable/bin/java -agentpath:/usr/lib/jvm/zulu-9-amd64/lib/libjdwp.so=transport=dt_socket,server=y,suspend=y,address=8000 -m executable
The result was:
ERROR: This jdwp native library will not work with this VM's version of JVMTI (11.0.0), it needs JVMTI 9.0[.0].
|It’s good to know that this type of protection is in place so that you don’t end up having to debug the debug configuration :||.|
In the end the command that allowed debugging to finally work was:
$] executable/bin/java -agentpath:/usr/lib/jvm/zulu-11-amd64/lib/libjdwp.so=transport=dt_socket,server=y,suspend=y,address=8000 -m executable
Conclusion and A Caveat
The exclusion of debugging support does feel like it might leave us susceptible to a maintenance issue where some time in the future we have a deployment to debug, but can’t conveniently find the exact matching JDK libs, or they aren’t deployed to the environment; leaving us to jump through hoops that may not be pleasant.
My suggestion would be to copy the two libraries necessary for debugging;
libdt_socket.so into the image
lib directory at build time:
$] cp /usr/lib/jvm/zulu-11-amd64/lib/libjdwp.so executable/lib/ $] cp /usr/lib/jvm/zulu-11-amd64/lib/libdt_socket.so executable/lib/
The debugging command line then becomes:
$] executable/bin/java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000 -m executable
Lastly, the space required by these two libraries (
26K respectively) I’d consider to be an insignificant price compared to the headache you probably end up finding yourself in in the future by not having included them in the image.