Class definition within groovy script - groovy

Is it possible to create class definitions within a groovy script?
I have a simple script example
class HelloWorld {
def name
def greet() {
"Hello ${name}"
}
}
def helloWorld = new HelloWorld()
helloWorld.name = "Groovy"
println helloWorld.greet()
but I get error like this
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script1.groovy: 1: Class definition not expected here. Please define the class at an appropriate place or perhaps try using a block/Closure instead. at line: 1 column: 1. File: Script1.groovy # line 1, column 1.
class HelloWorld {
^
1 error
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309) ~[groovy-all-2.4.3.jar:2.4.3]
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:943) ~[groovy-all-2.4.3.jar:2.4.3]
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:590) ~[groovy-all-2.4.3.jar:2.4.3]
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:566) ~[groovy-all-2.4.3.jar:2.4.3]
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:543) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:297) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:267) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:692) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyShell.parse(GroovyShell.java:704) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyShell.parse(GroovyShell.java:740) ~[groovy-all-2.4.3.jar:2.4.3]
at groovy.lang.GroovyShell.parse(GroovyShell.java:731) ~[groovy-all-2.4.3.jar:2.4.3

This code works fine. You can see for yourself here.

Yes, in general it's normal to create class definitions within a script.
I'm not able to reproduce your error, and you don't say how you're running this code. I'm guessing you're trying to use a Groovy script to configure some product (Mule has this feature, for instance). Your problem would seem to be specific to the thing you're trying to configure.
If I try to run the script in groovysh I get "Unknown property" when it tries to define the helloWorld variable using def.
In groovyconsole it works fine, as Dónal says.
If I take your code and put it in a file called HelloWorld.groovy and run that from the command line:
groovy HelloWorld.groovy
then I get
C:\Users\ndh\HelloWorld.groovy: 1: Invalid duplicate class definition of class HelloWorld : The source C:\Users\ndh\HelloWorld.groovy contains at least two definitions of the class HelloWorld.
One of the classes is an explicit generated class using the class statement, the other is a class generated from the script body based on the file name. Solutions are to change the file name or to change the class name.
# line 1, column 1.
class HelloWorld {
^
1 error
Changing the name of the file to notHelloWorld.groovy works:
c:\Users\ndh>groovy notHelloWorld.groovy
Hello Groovy
but this is not your problem because the stacktrace shows your filename to be Script1.
When I write a script I put the main logic at the top and put helper functions and class definitions toward the bottom. That's just how I organize the code for readability, though.

Related

how to share data between groovy files?

I have one common groovy file that contains few const variables and functions...
and I also have more groovy files with pipelineJob that use the variables and functions from the common file
what is the best way to import all the data from the common file to the other files?
I have not tested this with Jenkins, but if Jenkins executes the Groovy script as if by invoking groovy -cp .... myScript.groovy it should work:
utils.groovy:
// notice there's no "def", otherwise the def would be local only
name = 'Joe'
class MyUtils {
static String greeting(String name) {
"Hello $name"
}
}
src/main.groovy
def shell = new GroovyShell(getBinding())
shell.evaluate(new File('utils.groovy'))
println MyUtils.greeting(name)
Running it:
$ groovy src/Main.groovy
Hello Joe
Because the Script base class by default also has an evaluate method, your can actually just call that instead of using a GroovyShell and the result should be identical:
src/main.groovy
evaluate(new File('utils.groovy'))
println MyUtils.greeting(name)
If it doesn't work it's because the Script base class has been changed , probably... the first approach should work in all cases.

How imported class work in Groovy Closure when calling it?

I have a Groovy Closure which uses some imported class like:
import com.XXX
Closure test = { a -> XXX(a) }
test('some str')
How the imported class XXX work inside closure test since I never defined the XXX in test.
In this case delegate and owner are point to current script and still not figure out how import work.
Thanks
This example works, maybe look at how you specify the import statement's package structure:
assert org.apache.commons.lang3.text.WordUtils.capitalizeFully('groovy closure') == 'Groovy Closure'
import org.apache.commons.lang3.text.WordUtils
Closure test = { a -> WordUtils.capitalizeFully(a) }
assert test('groovy closure') == 'Groovy Closure'
I finally figured out this is a Java related question.
"import" key word in Java is kind of a syntax sugar which let you claim a class without full path name. And when the class file be compiled, class name will be replaced by full path of import by compiler.
So in my case, XXX will be compiled to com.XXX inside Closure(does not matter it is a Java or Groovy class), and it will work in any class which been called.

Groovy DSL non-deterministic behavior

I'm using groovyConsole to see the generated class code of the following two Groovy scripts:
process test{
output:
file 'output.txt' into b, c
"""
echo hello
"""
}
Here is its generated code:
And here is the second script that differs from the previous one - this one uses parentheses in file() method invocation:
process test{
output:
file('output.txt') into b, c
"""
echo hello
"""
}
And this is generated code from the second script:
As you can see, process is no longer a function but a variable. It confuses me because I would expect that in both cases process should be seen as a function call. Is this an expected behavior? Do I miss something?

Why do I get "Multiple markers at this line: Groovy expecting EOF found 'for'"?

The Error in the Question was generated by the following code
package ian.eg.learn
class ReadXMLfile {
def customers = new XmlSlurper().parse(new File("C:\\Users\\IBM_ADMIN
\\Documents\\customers.xml"))
for (customer in customers.corporate.customer){
println "${customer.#name} works for ${customer.#company}"
}
}
I am using a regular "for" and I don't see why the compiler is having a problem
I don't know what version of Groovy you're using so the exact error message could vary, but you cannot just write statements like that anywhere in your class, so the compiler expects something else in place of your for statement.
Example:
class Xxx {
println("yoo")
}
Gives:
unexpected token: println # line 2, column 3.
println("yoo")
^
You need to move that code in a method, or an init block... anywhere but not directly in class body.

No suitable ClassLoader found for grab while Instantiating a class

I have created two groovy scripts as below. One script has a class which is instantiated in the other script. Both are in default package.
When I'm trying to run ImportGpsData.groovy I'm getting the following exception...
Caught: java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
at ImportGpsData$_run_closure1.doCall(ImportGpsData.groovy:10)
at ImportGpsData.run(ImportGpsData.groovy:6)
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at DateParser.<clinit>(DateParser.groovy)
... 2 more
ImportGpsData.groovy
def file = new File('fells_loop.gpx')
def slurper = new XmlSlurper()
def gpx = slurper.parse(file)
gpx.rte.rtept.each {
println it.#lat
println it.#lon
def parser = new DateParser()
println parser.parse(it.time.toString())
}
Dateparser.groovy
#Grapes(
#Grab(group='joda-time', module='joda-time', version='2.3')
)
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
class DateParser {
def String parse(time){
def printableTime = new DateTime(time)
def format = DateTimeFormat.forPattern('MM/dd/yyyy - hh:mm aa')
return printableTime.toString(format)
}
}
I've found some other StackOverFlow questions that dealt with No Suitable classloader found for grab error. One answer suggested using #GrabConfig(systemClassLoader=true) inside #Grapes statement however adding it is resulting in compilation error, I'm getting error unexpected token # in line two.
#Grapes([
#Grab(group='joda-time', module='joda-time', version='2.3')
#GrabConfig( systemClassLoader=true )
])
Above way of using it gave unexpected token # found in line 3...
Adding a comma before #GrabConfig is giving the below error
Multiple markers at this line
- Groovy:Invalid duplicate class definition of class DateParser : The source F:\GroovyEclipses\src\DateParser.groovy contains at least two definitions of the class DateParser.
- General error during conversion: No suitable ClassLoader found for grab java.lang.RuntimeException: No suitable ClassLoader found for grab
After further analysis, I have figured that I'm getting this error when ever I user #Grapes and #Grab in any of my scripts. However I have to use them to work with joda-time.
Not sure if you were able to resolve this, if not then try to compile the class file first:
groovyc Dateparser.groovy
and then do
groovy ImportGpsData.groovy
should work.

Resources