42

File1.groovy

def method() {
   println "test"
}

File2.groovy

method()

I want to load/include the functions/methods from File1.groovy during runtime, equals to rubys/rake's load. They are in two different directories.

Arturo Herrero
  • 12,772
  • 11
  • 42
  • 73
ptomasroos
  • 1,129
  • 1
  • 9
  • 18

6 Answers6

36

If you don't mind the code in file2 being in a with block, you can do:

new GroovyShell().parse( new File( 'file1.groovy' ) ).with {
  method()
}

Another possible method would be to change file1.groovy to:

class File1 {
  def method() {
    println "test"
  }
}

And then in file2.groovy you can use mixin to add the methods from file1

def script = new GroovyScriptEngine( '.' ).with {
  loadScriptByName( 'file1.groovy' )
} 
this.metaClass.mixin script

method()
tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • Another question, short one related. If i want to run a script as it was intended, script not class. Not invoking a method. Lika run or execute method. – ptomasroos Jan 26 '12 at 07:59
  • You probably want [GroovyShell.evaluate](http://groovy.codehaus.org/api/groovy/lang/GroovyShell.html#evaluate(java.io.File)) – tim_yates Jan 26 '12 at 08:27
  • @tim_yates I think you should change loadScriptByName( 'file1.groovy' ) to loadScriptByName( 'file1' ) – despot Jul 22 '13 at 11:39
  • @despot really? I don't think it should be – tim_yates Jul 23 '13 at 08:42
  • Your solution got me on the right path but it isn't not quite right. You don't need to add a class inside the script your load.. I will give my answer. – Christian Bongiorno Apr 23 '18 at 04:09
22

You can evaluate any expression or script in Groovy using the GroovyShell.

File2.groovy

GroovyShell shell = new GroovyShell()
def script = shell.parse(new File('/path/file1.groovy'))
script.method()
Arturo Herrero
  • 12,772
  • 11
  • 42
  • 73
  • Do you know if its possible to get the methods as in the current script. this += script. If you understand. This is more what I'm after. – ptomasroos Jan 25 '12 at 15:45
  • 1
    calling method is wrong. it should be script.invokeMethod("method", args) assuming 'method' is static. – codeDr Dec 06 '13 at 15:40
  • This is working nicely for me. `file1.groovy` neither needs to be an actual class nor have its methods being static. – Matthew Wise Jun 26 '14 at 15:07
15

It will be easiest if file1.groovy is an actual class class File1 {...}.

Given that, another way to do it is to load the file into the GroovyClassLoader:

this.class.classLoader.parseClass("src/File1.groovy")

File1.method() 

File1.newInstance().anotherMethod()
clmarquart
  • 4,721
  • 1
  • 27
  • 23
  • 2
    you can also say def script = this.class.classLoader.parseClass("..."); def object = script.newInstance() – codeDr Dec 06 '13 at 15:41
  • I've been using this also (in Jenkins) and it seems this is eating up PermSpace. See here: http://stackoverflow.com/questions/24169976/understanding-groovy-grails-classloader-leak http://stackoverflow.com/questions/712187/troubleshooting-grails-groovy-memory-leaks Thus I wonder if this should really be recommended for server apps. – Axel Heider Apr 13 '16 at 22:16
  • There is a quirk: so would be btter to consider a different way to call parseClass, and use the one with `.parseClass(new File(".."))` See http://stackoverflow.com/questions/13993611/why-cant-i-instantiate-a-groovy-class-from-another-groovy-class – Kuzeko Feb 22 '17 at 14:18
12

I am late on this but. This is how we have been achieving what you were asking. So, i have a file1.gsh like so:

File1:

println("this is a test script")

def Sometask(param1, param2, param3)
{
    retry(3){
        try{
            ///some code that uses the param
        }
        catch (error){
            println("Exception throw, will retry...")
            sleep 30
            errorHandler.call(error)
        }
    }
}

return this;

And in the other file, these functions can be accessed by instantiating first. So in file2.

File2:

def somename
somename = load 'path/to/file1.groovy'
 //the you can call the function in file1 as

somename.Sometask(param1, param2, param3)
OK999
  • 1,353
  • 2
  • 19
  • 39
  • 4
    Working perfectly on a Jenkins pipeline! (Note: At first, I had forgotten the `return this` at the end of my script, so `load` was returning `null`). – JonasVautherin Oct 21 '16 at 12:14
  • `load 'path/to/file'` works flawlessly, don't know why this was so hard to find, although if I remember correctly, it didn't work when prefixed with `./` – Michael Allan Jackson Jul 26 '17 at 20:57
  • What 'this' returns? It should be like a Script object or sth in the groovy SDK. (I already checked the 'Script' object but no 'load' method in it.) – stdout Jan 04 '18 at 13:38
  • 9
    'load' is only meaningful when running in a jenkins context. It is not part of the groovy language. – Hoobajoob Mar 23 '18 at 19:23
  • 3
    As @Hoobajoob mentioned above, this is a Jenkins pipeline DSL, not a Groovy language feature. – haridsv Sep 16 '18 at 05:32
  • Doesn't work for JobDSL scripts, so it's a solution ONLY for Jenkins pipelines. – BVengerov Jul 12 '19 at 10:06
  • This is exactly what I was looking for. Thanks! – Yeikel Aug 21 '19 at 14:07
5

Here is what I'm using.

1: Write any_path_to_the_script.groovy as a class

2: In the calling script, use:

def myClass = this.class.classLoader.parseClass(new File("any_path_to_the_script.groovy"))
myClass.staticMethod()

It's working in the Jenkins Groovy script console. I have not tried non-static methods.

Szymon
  • 42,577
  • 16
  • 96
  • 114
filamoon
  • 103
  • 1
  • 6
  • 2
    (same comment as to a similar answer above) I've been using this also (in Jenkins) and it seems this is eating up PermSpace. See here: http://stackoverflow.com/questions/24169976/understanding-groovy-grails-classloader-leak http://stackoverflow.com/questions/712187/troubleshooting-grails-groovy-memory-leaks Thus I wonder if this should really be recommended for server apps. – Axel Heider Apr 13 '16 at 22:17
3

The answer by @tim_yates that uses metaClass.mixin should have worked without needing any changes to file1.groovy (i.e., mixin with the script object), but unfortunately there is a bug in metaClass.mixin that causes a SO error in this scenario (see GROOVY-4214 on this specific issue). However, I worked around the bug using the below selective mixin:

def loadScript(def scriptFile) {
   def script = new GroovyShell().parse(new File(scriptFile))
   script.metaClass.methods.each {
       if (it.declaringClass.getTheClass() == script.class && ! it.name.contains('$') && it.name != 'main' && it.name != 'run') {
           this.metaClass."$it.name" = script.&"$it.name"
       }
   }
}

loadScript('File1.groovy')
method()

The above solution works with no changes being needed to File1.groovy or the callers in File2.groovy (except for the need to introduce a call to loadScript function).

haridsv
  • 9,065
  • 4
  • 62
  • 65