user140547
user140547

Reputation: 8200

How to use Groovy dynamic objects to call methods requiring concrete types (without referencing them)

Given the following code:

def model = readMavenPom file: 'pom.xml'
dep = [
   groupId : "org.foo",
   artifactId :  "bar",
   version : "1.0" ]

I would like to call Model.addDependency

model.addDependency(dep)

This gives the error

Caught: groovy.lang.MissingMethodException: No signature of method: org.apache.maven.model.Model.addDependency() is applicable for argument types: (java.util.LinkedHashMap) values: [[groupId:org.foo, artifactId:bar, version:1.0]]

Now it is possible to do

model.addDependency(dep as Dependency)

When referencing the class. And it works without problems. locally.

Unfortunately, I have to execute that on Jenkins, and I am running into this issue. So basically, if I reference the Dependency class, I running into some class loading issues. There is a recommendation to use "dynamic typing".

So it is possible to call addDependency without referencing that class?

Upvotes: 1

Views: 427

Answers (2)

AntoineT
AntoineT

Reputation: 29

I created a ticket for that https://issues.jenkins.io/browse/JENKINS-70717 . Thanks for the workaround, I added that there.

Upvotes: 0

Corrodias
Corrodias

Reputation: 784

The problem is that the classloader that loaded the Model class (let's call it "A") is not the same one as the script tries to use when you refer to the Dependency class ("B"). So you can create a Dependency-B with your classloader-B, but a Model-A demands a Dependency-A.

We can use reflection to get a reference to the Dependency-A class, then create an instance of it using the same property map, like so:

def model = readMavenPom file: 'pom.xml'
dep = [
   groupId : "org.foo",
   artifactId :  "bar",
   version : "1.0" ]
model.addDependency(model.&addDependency.parameterTypes[0].n‌​ewInstance(dep))

In Groovy, &addDependency is a method reference, reflection style. We know for certain that there's only one method by that name and it has only one parameter, so we just take the first parameter's class and call newInstance, which works the same way as the "as" keyword would have.

Upvotes: 1

Related Questions