Error Caught: groovy.lang.GroovyRuntimeException: This script or class could not be run - groovy

Recently encountered with this problem- when i had two class in one groovy class if main method class is not on the top i encountered this problem.
class Book {
private String name
void setName(String bookName) {
name=bookName
print "Book Name => "+bookName+"\n";
}
String getName(){
return name;
}
}
class TestClass {
static main(args) {
Book t = new Book();
t.setName("First Book");
print "book title=>"+t.getName()+"\n"
}
}
But if change the order of these two class than there is no error, does it mean main method class should be on top in Groovy ?

Yes, order of classes matter in a groovy script. If you parse a groovy script and check its class name, it would the top level class and not the one with main method or one that have same name as the name of the file. It could be a concrete class, an abstract class, an enum, an interface or trait.
Lets see your case. We are going to put your code inside a GString and then will try to parse it using our own GroovyClassLoader.
String script = """
class Book {
private String name
void setName(String bookName) {
name=bookName
print "Book Name => "+bookName+"\\n";
}
String getName(){
return name;
}
}
class TestClass {
static main(args) {
Book t = new Book();
t.setName("First Book");
print "book title=>"+t.getName()+"\\n"
}
}
"""
GroovyClassLoader loader = new GroovyClassLoader()
GroovyCodeSource codeSource = new GroovyCodeSource(script, "MyClass", GroovyShell.DEFAULT_CODE_BASE)
println loader.parseClass(codeSource)
When you will execute this code it will print class Book. Because this is the first available class in your script.
The exception you are getting is because that your top level class doesn't have a main method and neither your script have something to execute after you have loaded your classes. One solution is to move TestClass at top or just add another line at the end of the file TestClass.main() and it will execute without any issues.

Related

Does Groovy "as" operator create a subclass at run time for User defined classes?

In Groovy When I write the below code in a groovy script.
class Emp {
public String getId() {
return "12345";
}
}
def coercedInstance = [
getId: {
"99999"
}
] as Emp
println new Emp().getId()
println coercedInstance .getId()
Using the as Operator here, am I creating a sub class of the actual Emp class at runtime and providing the method body at run time?
I have seen other stack overflow articles and i have learnt that Groovy uses DefaultGroovyMethods.java & DefaultTypeTransformation.java to do the coercion. But could not figure out if it was subclassing or not.
Yes, an as operator creates an object which type is a subclass of the target class. Using DefaultGroovyMethods.asType(Map map, Class clazz) generates (in a memory) a proxy class that extends given base class.
class Emp {
public String getId() {
return "12345";
}
}
def coercedInstance = [
getId: {
"99999"
}
] as Emp
assert (coercedInstance instanceof Emp)
assert (coercedInstance.class != Emp)
assert (Emp.isAssignableFrom(coercedInstance.class))
println coercedInstance.dump() // <Emp1_groovyProxy#229c6181 $closures$delegate$map=[getId:coercion$_run_closure1#7bd4937b]>
What happens in your case specifically is the following:
The asType method goes to line 11816 to execute ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(map, clazz);
In the next step, ProxyGeneratorAdapter object gets created.
In the last step, adapter.proxy(map,constructorArgs) gets called to return a newly generated class that is a proxy of the base class.

Load groovy classes/scripts dynamically without compilation?

I have a set of groovy scripts in package hierarchy. I have 1 main script, from which I want to call others. For example I have these scripts (with public classes/interfaces of the same name in them):
package.MainScript
package.MyInterface;
package.utils.MyInterfaceImpl1 //implements MyInterface
package.utils.MyInterfaceImpl2 //implements MyInterface
Is there a way to call one script from the other without knowing called class name at compile time? I mean to do something like dynamic class loading like:
class MainScript {
public static void main (String[] args) {
MyInterface instance = Class.forName("package.utils.Util1");
}
}
Yeah! Groovy is a dynamic language. You can create class instance dynamically.
package.MyInterface
class MyInterfaceImpl1 {
def greet() {
"Hello"
}
}
package.MyInterface
class MyInterfaceImpl2 {
def greet() {
"Hi!"
}
}
def name = 'MyInterfaceImpl1' // Choose whatever you want at runtime
def className = Class.forName("MyInterface.$name")
def instance = className.newInstance()
assert instance.greet() == 'Hello'

"def Type" declarations in groovy classes

Suddenly I've realized that I can write
class Person {
def String name
}
My question is - what is the difference between code provided above and classic:
class Person {
String name
}
Why first form even exists?
There's no difference. Adding def to the beginning of a type definition does nothing. However, it is allowed by the parser.
One way to check stuff like this, is to fire up the groovyConsole, and run the AST Browser (which for the script)
class Person {
def String name
}
Shows:
public class Person extends java.lang.Object {
private java.lang.String name
}

Cannot dynamically change or add methods in a class using GroovyClassLoader

I try to dynamically change and add methods of a class defined in a groovy script from another groovy script but cannot figure out why it works if I use the classname directly in .metaClass. but not if I load the class using the GroovyClassLoader(which I need to do!).
In one file 'MyTest.groovy' I have
class MyTest {
public void setUp() {
println "SET UP"
}
public void tearDown() {
println "TEAR DOWN"
}
public void testA() {
println "testA"
}
}
and another file 'suite.groovy' contains
#!/usr/bin/env groovy
public class MyTestSuite {
// For debug
public static printClassLoader(Class cls) {
ClassLoader loader = cls.classLoader
while (loader) {
println loader.class
println loader.URLs.join("\n")
println "\n\n"
loader = loader.parent
}
}
public static void suite() {
// First method to define my class (change/addition of methods works)
//Class testClass = MyTest
// Second way to define my class (change/addition of methods doesn't work)
ClassLoader parent = MyTestSuite.class.getClassLoader();
GroovyClassLoader gcl = new GroovyClassLoader(parent);
Class testClass = gcl.parseClass(new File("MyTest.groovy"));
printClassLoader(testClass)
testClass.metaClass.setUp = {-> println "I'm your new setUp()" ;}
testClass.metaClass.newTest = {-> println "I'm your new newTest()" ; }
}
}
MyTestSuite.suite()
MyTest aTest = new MyTest()
aTest.setUp()
aTest.newTest()
With the first method I get the expected result:
I'm your new setUp()
I'm your new newTest()
But with the second one I get
SET UP
Caught: groovy.lang.MissingMethodException: No signature of method: MyTest.newTest() is applicable for argument types: () values: []
Possible solutions: testA(), getAt(java.lang.String), wait(), inspect(), every()
at suite.run(suite.groovy:34)
Interestingly modifying an existing method doesn't work but there is no exception but trying to add a new one throws one.
I reckon it has to do something with the class loaders used but couldn't figure what exactly and what to change!
For the first version here are the class loaders called:
class groovy.lang.GroovyClassLoader$InnerLoader
class groovy.lang.GroovyClassLoader
class org.codehaus.groovy.tools.RootLoader
class sun.misc.Launcher$AppClassLoader
class sun.misc.Launcher$ExtClassLoader
For the second version:
class groovy.lang.GroovyClassLoader$InnerLoader
class groovy.lang.GroovyClassLoader
class groovy.lang.GroovyClassLoader$InnerLoader
class groovy.lang.GroovyClassLoader
class org.codehaus.groovy.tools.RootLoader
class sun.misc.Launcher$AppClassLoader
class sun.misc.Launcher$ExtClassLoader
Any idea welcome!
Franck
The new GroovyClassLoader is not the Groovy classloader of the current thread.
It will be disposed after the suite() method finishes - along with its own Class and MetaClass associations.
However, this works:
Class testClass = gcl.parseClass(new File("MyTest.groovy"));
testClass = Class.forName(testClass.getName())

Groovy expando metaclass

I've developed a Class that has some methods that augment Integer, it mainly lets me do this:
def total = 100.dollars + 50.euros
Now I have to extend Integer.metaClass doing something like this:
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
I tried putting that at the bottom of the file, before the Money class declaration, but the compiler says that a class Named Money already exists, I know why it happens (because groovy creates a class with the name of the file with an empty static void main to run this code).
I also tried using a static block inside the class like this:
static {
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
}
This neither works.
A third solution would be to change the file name (like MoneyClass.groovy) and keep the class name (class Money) but that seems a bit weird.
Is there anything else I can do? Thanks.
Just put it in any method of any class maybe a bean TypeEnhancer.groovy:
public class TypeEnhancer {
public void start() {
Integer.metaClass.getDollars() = {-> Money.Dollar(delegate) }
}
public void stop() {
Integer.metaClass = null
}
}
Just create and initalize by calling start(): new TypeEnhancer().start();.
To disable the enhancement, call new TypeEnhancer().stop();. The bean can also used as Spring bean.

Resources