Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How come my pom.xml dependency isn't found for my Jenkins plugin?

My pom.xml has this dependency:

<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
    <classifier>jdk15</classifier>
</dependency>

When I use the XMLSerializer it throws an exception: java.lang.NoClassDefFoundError: nu/xom/Node

If I run the class locally and add the JAR to my classpath, everything works as expected. I'm running this class as a Jenkins plugin so I don't expect to be manually defining classpath - I figured that's what Maven should be handling.

It's important to note that Jenkins plugins require me uploading an hpi file that is created from Maven. It is not running based on the output jar. If I go on Jenkins box and manually put the xom JAR into WEB-INF/libs, it works. But obviously that means this plugin wouldn't for other people, which is self-defeating.

Here is minimal code causing error: https://github.com/DaveStein/parser-sample

The Readme has exact repro steps.

Note on chosen answer The PR to my sample repo got me most of the way to where I needed to be. I did have a few other issues that had to get resolved, but the JSONObject conflict was the core problem. I took out all GlobalConfiguration as Jesse's PR suggested. The only other issue that might concern a future viewer was some glitch when using xom as explicit dependency while also using a higher version than 1.626 for org.jenkins-ci.plugins at the time of this post.

like image 247
Dave Stein Avatar asked Sep 01 '17 15:09

Dave Stein


1 Answers

Jenkins core bundles json-lib. (A forked copy, not that it matters for purposes of this question.) It does not bundle the optional dependency¹ XOM, whatever that is. When your plugin loads XmlSerializer.class, it gets defined by the class loader for Jenkins core, which then attempts to link against classes such as nu.xom.Node. Since this is not available in the defining loader of XmlSerializer—the Jenkins core class loader (more or less jenkins.war!/WEB-INF/lib/*.jar)—you get an error. The fact that a class by that name is accessible in your plugin class loader is of no import, excuse the pun.

If your plugin needs to use its own versions of classes which are normally bundled in Jenkins core and exposed to plugins implicitly, then it needs to not only bundle those JARs (a regular compile-scoped Maven dependency suffices for that purpose), but to also use the pluginFirstClassLoader option. Before attempting to do so, you had better understand Java class loading semantics thoroughly, or you will be lost in a maze of cryptic² ClassCastExceptions and LinkageErrors.

By the way the mvn hpi:run command normally used to test plugin code iteratively does not simulate a realistic class loading regime. So if you are using pluginFirstClassLoader or any other tricks in this space, always double-check the resulting class loading behavior by (re-)installing an *.hpi in a sample Jenkins instance, for example using /pluginManager/advanced, or the install-plugin CLI command. Judging by your description, you were already doing that (and perhaps unaware of hpi:run).

¹The original sin here is use of optional dependencies. json-lib should rather have defined a distinct artifact json-lib-xom with hard dependencies on json-lib and xom. That would ensure that any given class loader can either see XmlSerializer and its dependencies, or neither.

²No progress on JDK-6273389, alas. Marked as a duplicate, but what it is a duplicate of, I am not sure. Theoretically Java 9 modules make questions like this obsolete—by imposing such onerous restrictions that applications like Jenkins could not use that module system to begin with.

like image 104
Jesse Glick Avatar answered Oct 29 '22 13:10

Jesse Glick