3.7. Nesting Scripts: Calling Scripts from Scripts

To properly structure larger scripts it can be helpful to spread functionality over several scripts. The above mentioned scriptService provides simple helper methods to call scripts from within scripts. For this, consider the following script which does nothing but provide two methods

def caesarEncode(k, text) { 
    (text as int[]).collect { it==' ' ? ' ' : (((it & 0x1f) + k - 1).mod(26) + 1 | it & 0xe0) as char }.join()
}   
def caesarDecode(k, text) { caesarEncode(26 - k, text) }

Assume this is stored in a file called lib.groovy. For the interested reader, this is a simple implementation of the Caesar cipher found here: https://www.rosettacode.org/wiki/Caesar_cipher#Groovy (note this should not be used for actual encryption). Now, to load these helper methods we can use the following, which we store in a file called nestingTest.groovy

GLOBALS.exec('lib.groovy')

def plain = 'The quick brown fox jumps over the lazy dog'
def key = 6
def cipher = caesarEncode(key, plain)

tout.println plain
tout.println cipher
tout.println caesarDecode(key, cipher)

If you now run this script you should get the following output

reportserver$ exec nestingTest.groovy
The quick brown fox jumps over the lazy dog
Znk waoiq hxuct lud pasvy ubkx znk rgfe jum
The quick brown fox jumps over the lazy dog

The example above can be found here: https://github.com/infofabrik/reportserver-samples/tree/main/src/net/datenwerke/rs/samples/tools/nesting.

Note that ReportServer uses internal script caching for performance optimization. When developing nested scripts, if you get an old script version when running, you can manually clear scripting cache with the clearInternalScriptCache terminal command.

Note you can use relative paths for lib.groovy. For example:

GLOBALS.exec('../crypt/libs/lib.groovy')

or

GLOBALS.exec('libs/lib.groovy')

Besides the simple exec method that we have used above the GLOBALS object provides a second exec method that takes as second parameter an argument string that is passed to the script.

3.7.1. Using classes in nested scripts

When you need to use classes in nested scripts, you can not use the

GLOBALS.exec('../crypt/libs/lib.groovy')

method explained above directly. With other words, this would not work if B contains a class definition:

GLOBALS.exec('/fileserver/bin/B.groovy')
def b = new B()
b.prepareString()

Groovy would try to compile your script, but cannot find B's class definition during compilation time.

In this case, you can use the methods GLOBALS.loadClass() and GLOBALS.loadClasses() for loading your class definitions. Using GLOBALS.newInstance() allows you to create instances as shown below. Finally, you can call your instance's methods, in this example prepareString().

def bClass = GLOBALS.loadClass('B.groovy', 'net.datenwerke.rs.samples.tools.nesting.nestedclass.B')
def bInstance = GLOBALS.newInstance(bClass)
return bInstance.prepareString()"

A complete example using two levels (A.groovy creating B objects creating C objects) can be found here: https://github.com/infofabrik/reportserver-samples/tree/main/src/net/datenwerke/rs/samples/tools/nesting/nestedclass.

The following example shows how to load classes which are defined in the same .groovy file (B and C are defined both in myLibraries.groovy): https://github.com/infofabrik/reportserver-samples/tree/main/src/net/datenwerke/rs/samples/tools/nesting/multipleclass