Skip to content

Instantly share code, notes, and snippets.

@tknerr
Last active March 30, 2024 06:56
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tknerr/42258e761f2a0f95a92b to your computer and use it in GitHub Desktop.
Save tknerr/42258e761f2a0f95a92b to your computer and use it in GitHub Desktop.
groovy script classpath problem

I have the following file structure:

Y:\tmp\test
|   main.groovy
|
\---impl
        FooImpl.groovy

This is impl/FooImpl.groovy:

package impl

class FooImpl {
	def foo() {
		"hello?!"
	}
}

And main.groovy is as simple as that:

println new impl.FooImpl().foo()

When I'm executing main.groovy from within the Y:\tmp\test dir everything works as expected:

Y:\tmp\test>groovy main.groovy
hello?!

However, when running it from a different directory (e.g. one level above), it will not find FooImpl because it's not on the classpath:

Y:\tmp>groovy test\main.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Y:\tmp\test\main.groovy: 2: unable to resolve class impl.FooImpl
 @ line 2, column 9.
   println new impl.FooImpl().foo()
           ^

1 error

I'm aware this can be fixed by supplying a proper classpath:

Y:\tmp>groovy -cp test\ test\main.groovy
hello?!

However, the process calling my main.groovy script is not under my control, so I can not change the classpath unfortunately :-(

I was hoping to be able to set the classpath dynamically as described here, so I modified main.groovy to:

this.class.classLoader.rootLoader.addURL( new URL("file:///y:/tmp/test/") )
println new impl.FooImpl().foo()

But it still doesn't work, results in the same error:

Y:\tmp>groovy test\main.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Y:\tmp\test\main.groovy: 4: unable to resolve class impl.FooImpl
 @ line 4, column 9.
   println new impl.FooImpl().foo()
           ^

1 error

Any ideas what I'm doing wrong here?

@tknerr
Copy link
Author

tknerr commented Oct 9, 2014

Got it thanks to this answer on stackoverflow:
https://stackoverflow.com/questions/306139/how-do-i-include-jars-in-a-groovy-script#comment11198154_306168

The issue is that imports are resolved prior to the script running. So if we delay the instantiation e.g. via reflection it will work:

this.class.classLoader.rootLoader.addURL( new URL("file:///y:/tmp/test/") )
println Class.forName("impl.FooImpl").newInstance().foo()

w00t!

@agray
Copy link

agray commented Apr 28, 2016

this.class.classLoader.rootLoader.addURL( new URL("file:///y:/tmp/test/") )

Confirmed that File exists but doesn't work in Jenkins ActiveChoices plugin. All I see is the fall back script running.

If I exectue the same line in Groovy Postbuild step I see

java.lang.NullPointerException: Cannot invoke method addURL() on null object
at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:77)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:45)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:32)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at Script1.run(Script1.groovy:8)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:580)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:618)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:589)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:168)
at org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildRecorder.perform(GroovyPostbuildRecorder.java:362)
at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:782)
at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:723)
at hudson.model.Build$BuildExecution.post2(Build.java:185)
at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:668)
at hudson.model.Run.execute(Run.java:1763)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
at hudson.model.ResourceController.execute(ResourceController.java:98)
at hudson.model.Executor.run(Executor.java:410)

@juliantcook
Copy link

@agray - works for me if I call addUrl() on classLoader.this.class.classLoader.addUrl(url)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment