I've been struggling for a while now on how to best approach
dependency management in Jenkins. One of our key pain points has been
in shared libraries like Jackson, HttpComponents, AsyncHTTPClient, and
various other commonly used libraries. As we've already found, simply
including these common dependencies in Jenkins itself leads to
problems such as never being able to upgrade that library again if it
breaks backward compatibility in the slightest.
In order to address these types of issues, I have four different proposals:
1. Shade in third party dependencies of Jenkins core into Jenkins with
a package rename. This will allow core to use the dependencies, but
plugins will still need to include the dependencies explicitly. This
could potentially be combined with a compatibility shim for plugins
that try to load a class that doesn't exist. This would allow for the
core dependencies to upgrade more easily as well. This is certainly a
hacky way of approaching it, but it's fairly common for low level
libraries to package rename their dependencies to avoid JAR hell.
2. Create facade APIs for common third party libraries such as a JSON
API, an HTTP client, etc. This approach could at least work the most
Jenkins-native way, though it seems like a rather large task.
3. Adopt JakartaEE (formerly known as JavaEE) tech via microprofile
<
https://microprofile.io/> or similar. This is fairly similar to #2,
but instead, we delegate the design of these facade APIs to the
experts. I like this idea myself, though I'm not sure how well this
works with Jetty and Tomcat.
4. Utilize OSGi in the plugin system. We can dynamically add OSGi
metadata via bndtools at load time, and the hpi plugin could be
updated to include those metadata for faster startup. Hell, I'm pretty
sure the access modifier annotations could even be implemented via
OSGi bundle wiring. Based on my previous experience with OSGi, I
believe we would only be able to utilize up to and including the
module/bundle layer of OSGi. Higher level APIs from OSGi tend to have
duplicate functionality in Jenkins, so that's not necessary. The
advantages to this approach would be the ability to run multiple
versions of a library in the same JVM if necessary, the potential
ability to install and update plugins without restarting Jenkins (!),
and a proper shared library system we could use instead of needing to
package API plugins.
4.alt. I don't know enough about JPMS, but perhaps it can be used to
help enforce something similar? It would only be available in Jenkins
running on Java 9 or higher, though.
--
Matt Sicker
Senior Software Engineer, Jenkins Security, CloudBees