-
Bug
-
Resolution: Unresolved
-
Minor
-
None
-
Jenkins 2.235
It is not possible to use external third party libaries with optional dependencies properly in shared libraries for Jenkins.
I have a shared library which uses Commons Configurations 2 to read various configuration files, mostly written as YAML documents.
Commons configurations uses SnakeYAML to read YAML documents and the dependency to SnakeYAML is defined as optional as follows:
<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.26</version> <optional>true</optional> </dependency>
According to the documentation of Maven how optional dependencies work an optional dependency is not added by default to the classpath. If a person wants to the part of the library which depends on the optional dependency, it must add this dependency to his own POM.
As I was going to use Commons Configuration 2 in conjunction with SnakeYAML, I defined the following variable in vars/readConfig.groovy as follows:
@Grapes([ @Grab(group = "org.apache.commons", module = "commons-configuration2", version = "2.7"), @Grab(group = "org.yaml", module = "snakeyaml", version = "1.26") ]) import org.apache.commons.configuration2.BaseConfiguration import org.apache.commons.configuration2.Configuration import org.apache.commons.configuration2.YAMLConfiguration def call() { Configuration config = new BaseConfiguration(); YAMLConfiguration yamlConfiguration = new YAMLConfiguration(); }
Calling readConfig() from a shared Library results in a java.lang.ClassNotFoundException with the following message
java.lang.ClassNotFoundException: org.yaml.snakeyaml.DumperOptions at jenkins.util.AntClassLoader.findClassInComponents(AntClassLoader.java:1387) at jenkins.util.AntClassLoader.findClass(AntClassLoader.java:1342) at jenkins.util.AntClassLoader.loadClass(AntClassLoader.java:1089) at java.lang.ClassLoader.loadClass(ClassLoader.java:352) at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.privateGetPublicMethods(Class.java:2902) at java.lang.Class.getMethods(Class.java:1615) at java.beans.Introspector.getPublicDeclaredMethods(Introspector.java:1336) at java.beans.Introspector.getTargetMethodInfo(Introspector.java:1197) at java.beans.Introspector.getBeanInfo(Introspector.java:426) at java.beans.Introspector.getBeanInfo(Introspector.java:173) at groovy.lang.MetaClassImpl$15.run(MetaClassImpl.java:3313) at java.security.AccessController.doPrivileged(Native Method) at groovy.lang.MetaClassImpl.addProperties(MetaClassImpl.java:3311) at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:3288) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:260) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:302) ...
also checked the directory ~/.groovy/grapes for the presence of all needed jars and they are there.
jenkins@cb765137c926:~/.groovy$ find . -name "*.jar" ./grapes/commons-logging/commons-logging/jars/commons-logging-1.2.jar ./grapes/org.apache.commons/commons-configuration2/jars/commons-configuration2-2.7.jar ./grapes/org.apache.commons/commons-lang3/jars/commons-lang3-3.9.jar ./grapes/org.apache.commons/commons-text/jars/commons-text-1.8.jar ./grapes/org.yaml/snakeyaml/jars/snakeyaml-1.26.jar
To do a cross-check I wrote the following Groovy script and was able to execute it on my computer successfully.
@Grapes([ @Grab(group = 'org.apache.commons', module = 'commons-configuration2', version = '2.7'), @Grab(group = 'org.yaml', module = 'snakeyaml', version = '1.26'), @GrabConfig(systemClassLoader = true) ]) import org.apache.commons.configuration2.* println("Start") YAMLConfiguration y = new YAMLConfiguration() println y
I also checked if SnakeYAML is available at all with following code:
@Grapes([ @Grab(group = "org.apache.commons", module = "commons-configuration2", version = "2.7"), @Grab(group = "org.yaml", module = "snakeyaml", version = "1.26") ]) import org.apache.commons.configuration2.BaseConfiguration import org.apache.commons.configuration2.Configuration import org.apache.commons.configuration2.YAMLConfiguration import org.yaml.snakeyaml.DumperOptions import org.apache.commons.text.similarity.JaroWinklerDistance def call() { Configuration config = new BaseConfiguration(); //YAMLConfiguration yamlConfiguration = new YAMLConfiguration(); def d = new JaroWinklerDistance().apply("abc", "abcd") DumperOptions opt = new DumperOptions() //println opt.getClass().toString() }
This code is working as expected. That means that only Commons Configuration is not able to use the packages of SnakeYAML while it is possible to use classes from SnakeYAML's packages directly.
So, I am not able to guess the reason for this problem, as I am not so familiar with the internals of Jenkins. But it would be great to know if there is a way to get it working as intended.