Groovy cannot call Classloader (NullpointerException) - groovy

I have created the following Groovy Script to transform a JSON Document with a Java Library. But somehow I am not able to load the class from a jar that I need. I always get java.lang.ClassNotFoundException: de.is24.gis.geotools.CoordinateTransformer
The Jar file is in the same directory the groovy script is. I can not edit the way I call the groovy script. It is called automatically by a river.
import groovy.json.JsonSlurper
geo = new GeoTransformer()
geo.transform(ctx.document)
class GeoTransformer {
void transform(doc) {
this.getClass().classLoader.addURL(new File("gis-geotools-1.9.0.jar").toURL())
def CoordinateTransformer = Class.forName("de.is24.gis.geotools.CoordinateTransformer").newInstance();
def x = doc.realEstateCommonData.locationDto.geoCoordinateDto.xCoordinate;
def y = doc.realEstateCommonData.locationDto.geoCoordinateDto.yCoordinate;
def coords = CoordinateTransformer.transformFromLambertEuToWgs84(x,z)
println coords.getLatitude()
println coords.getLongitude()
def jsonObj = new JsonSlurper().parseText( '{"type" : "Point", "coordinates" : [' + coords.getLatitude() + ',' + coords.getLongitude() + ']}' )
doc.location = jsonObj
}
}

Not sure why you can't get to the rooLoader, it must be to do with how this Groovy script is executed.
You could try this (obviously untested)
class GeoTransformer {
void transform( doc ) {
def urlLoader = new GroovyClassLoader()
urlLoader.addURL( new File("gis-geotools-1.9.0.jar").toURL() )
def coordTransformer = Class.forName( "de.is24.gis.geotools.CoordinateTransformer",
true,
urlLoader ).newInstance()
def x = doc.realEstateCommonData.locationDto.geoCoordinateDto.xCoordinate;
def y = doc.realEstateCommonData.locationDto.geoCoordinateDto.yCoordinate;
def coords = coordTransformer.transformFromLambertEuToWgs84( x, z )
println coords.latitude
println coords.longitude
doc.location = [ type:'Point',
coordinates:[ coords.latitude, coords.longitude ] ]
}
}
I got rid of the JsonSlurper bit at the bottom, and just created the map directly (I assume doc.location needs to be a map)?
Edit:
This works in the Groovy Console:
def jar = new File( '/path/to/commons-collections-3.2.1.jar' )
def loader = new GroovyClassLoader()
loader.addURL( jar.toURL() )
def bag = Class.forName( 'org.apache.commons.collections.bag.HashBag',
true,
loader ).newInstance()
bag.add( 'tim' )
println bag
println bag.getClass().name
And prints:
[tim]
org.apache.commons.collections.bag.HashBag

I had the same problem when running groovy from java via GroovyShell.
This solution works for me and solves the dependency-loading problems that MeiSign mentions in tim_yates solution. Explanation follows:
def thisLoader = this.class.classLoader
// try the "proper" way to find the root classloader
def rootLoader = DefaultGroovyMethods.getRootLoader(thisLoader)
if (rootLoader == null) {
// Root classloader is not a groovy RootLoader, but we still need it,
// so walk up the hierarchy and get the top one (whose parent is null)
// When running from Java this is sun.misc.Launcher.ExtClassLoader
rootLoader = thisLoader
ClassLoader parentLoader = rootLoader.getParent()
while (parentLoader != null) {
rootLoader = parentLoader
parentLoader = parentLoader.getParent()
}
}
rootLoader.addURL(new File("gis-geotools-1.9.0.jar").toURL())
def CoordinateTransformer =
Class.forName("de.is24.gis.geotools.CoordinateTransformer",
true,
rootLoader).newInstance();
When running groovy from java using groovy.lang.GroovyShell.main, the this.classLoader is GroovyClassLoader.InnerLoader
When running groovy from command line groovy.bat, the class loader is org.codehaus.groovy.tools.RootLoader
When you call getRootLoader, it walks up the classloader hiearchy - with getParent() - until it finds an instance of a RootLoader. If it doesn't it finds null. (That's the NPE in the title of this question)
The problem is that when running from Java the heirarchy tops out at sun.misc.Launcher.ExtClassLoader which is clearly not a groovy class at all, let alone the groovy RootLoader.
Specifically the hiearchy is:
GroovyClassLoader.InnerLoader
--> GroovyClassLoader
---> sun.misc.Launcher.AppClassLoader
----> sun.misc.Launcher.ExtClassLoader
------> null
How it ends up that way is pretty obscure in GroovyMain (but if you really want to set it yourself there's a GroovyShell constructor that takes a ClassLoader).
Anyway, Tim's solution doesn't work in depth, because the new ClassLoader you are creating on the fly is used only to load that class and not subsequent classes. You really do need to add the classpath entries to the root classpath.
So I simply used the real root when the groovy root fails.
Here's the original code from org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport
/**
* Iterates through the classloader parents until it finds a loader with a class
* named "org.codehaus.groovy.tools.RootLoader". If there is no such class
* <code>null</code> will be returned. The name is used for comparison because
* a direct comparison using == may fail as the class may be loaded through
* different classloaders.
*
* #param self a ClassLoader
* #return the rootLoader for the ClassLoader
* #see org.codehaus.groovy.tools.RootLoader
* #since 1.5.0
*/
public static ClassLoader getRootLoader(ClassLoader self) {
while (true) {
if (self == null) return null;
if (isRootLoaderClassOrSubClass(self)) return self;
self = self.getParent();
}
}
private static boolean isRootLoaderClassOrSubClass(ClassLoader self) {
Class current = self.getClass();
while(!current.getName().equals(Object.class.getName())) {
if(current.getName().equals(RootLoader.class.getName())) return true;
current = current.getSuperclass();
}
return false;
}

Related

How can I use wrapped Groovy shell inside the groovy script?

I have a Java app through which I'm executing a Groovy Script, the problem occurs when my Script again has code to use the groovy shell to execute the Inner script.
something like this
import com.xx.WrappedGroovyShell
import java.lang.*
def scriptString = """
def test(str) {
str.toLowerCase() // This doesn't work as GString can't seem to be treated as a String
}
"""
try {
def script = WrappedGroovyShell.getInstance().getScript("Test1", scriptString)
def script2 = new GroovyShell().parse(scriptString)
def example = 1
def gstring = "OUR VALUE IS ${example}";
println script instanceof GroovyObject // Statement returning false
println script.test(gstring) // This throws an exception groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.runtime.GStringImpl.toLowerCase() is applicable for argument types: () values: []
println script2 instanceof GroovyObject // Statement returns true
println script2.test(gstring) //this one works
println "Success"
} catch (ex) {
println ex
}
return 0
The getScript method in WrappedGroovyShell.java is as simple as
public Script getScript(String scriptName, String scriptBody){
return new GroovyShell().parse(scriptBody);
}
I will be thankful if someone let me know why two different behavior.
new GroovyShell() creates a new groovy classloader with parent = GroovyShell.class.getClassLoader()
and if GString.class is unknown for parent classloader - then it will be loaded again.
two GString classes loaded by different classloaders are not equal that's why you could have unexpected exceptions like "method not found for parameter GString" or "can't cast GString to GString"
this code should work because it uses classloader of current groovy class
def scriptText='''
def test(str) {
str.toLowerCase()
}
'''
def gs = new GroovyShell(this.getClass().getClassLoader())
def script = gs.parse(scriptText)
def arg = "GSTRING ${123}"
println script.test(arg)

Skip MissingPropertyException while using GroovyShell.evaluate

Is there a way to skip MissingPropertyException while using GroovyShell.evaluate?
def sharedData = new Binding()
def shell = new GroovyShell(sharedData)
shell.evaluate("a=5; b=1") // works fine
// How to not get MissingPropertyException, or silently ignore property 'a'
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
I know about the Expando solution, is there a way to do it without defining a class?
A very minimal approach would be to override Binding.getVariable.
Note that this is very straight forward: "all exceptions" are ignored - you might want to have better logging or more accurate error handling.
import groovy.lang.*
class NoOpBinding extends Binding {
#Override
Object getVariable(String name) {
try {
return super.getVariable(name)
}
catch (Throwable t) {
println "Ignoring variable=`$name`"
return null
}
}
}
def shell = new GroovyShell(new NoOpBinding())
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
// → Ignoring variable=`a`
println shell.getVariable('b')
// → 1
You could do something like this:
def binding = [:].withDefault { }
def shell = new GroovyShell(binding as Binding)
shell.evaluate 'a; b=1'

Error: md5sum: stat '>': No such file or directory

I am trying to write a program that runs the Linux commands using Scala.
I have written a snippet of code to run the functionalities of md5sum command.
Code snippet
object Test extends App {
import sys.process._
case class md5sum_builder private(i: Seq[String]) {
println(i)
protected def this() = this(Seq(""))
def optionCheck() = new md5sum_builder(i :+ "-c")
def files(file: String) = new md5sum_builder(i :+ file)
def hashFile(hashfile: String) = new md5sum_builder(i :+ hashfile)
def assignment(operator: String) = new md5sum_builder(i :+ operator)
def build() = println(("md5sum" + i.mkString(" ")).!!)
}
object md5sum_builder {
def apply() = new md5sum_builder
}
md5sum_builder().files("text.txt").files("text1.txt").assignment(">").hashFile("hashes.md5").build()
}
When I try to run the command md5sum text.txt text1.txt > hashes.md5 using this program, it throws the error:
Error: md5sum: stat '>': No such file or directory
I don't know why. Any way to make it work?
Your interface doesn't appear to be well thought out. Notice that files(), hashFile(), and assignment() all do the same thing. So someone could come along and do something like this ...
md5sum_builder().assignment("text0.txt")
.hashFile("text1.txt")
.files(">") // <--shell redirection won't work
.assignment("hashes.md5")
.build()
... and get the same (non-functional) result as your posted example.
Here's a modification that corrects for that as well as allowing redirected output.
case class md5sum_builder private(i :Seq[String], outfile :String = "/dev/null") {
protected def this() = this(Seq.empty[String])
def optionCheck(file :String) = this.copy(i = i ++ Seq("-c", file))
def file(file: String) = this.copy(i = i :+ file)
def hashFile(file: String) = this.copy(outfile = file)
def build() = println(("md5sum" +: i).#|(Seq("tee", outfile)).!!)
}
Now the methods can be in almost any order and still get the expected results.
md5sum_builder().file("text0.txt")
.hashFile("hashes.md5")
.file("text1.txt")
.build()

Not able to read the system variable set from GebConfig.groovy file in spec file

I am using geb spock. I am trying to read the system variable from GebConfig file, however it returns the null value.
Below is my GebConfig.groovy file.
def final DEFAULT_BROWSER = "chrome"
def final DEFAULT_LANGUAGE = "nl" //"en" or "nl"
def browser = System.getProperty("geb.env")
//Default browser
if (!correctBrowser(browser)) {
browser = DEFAULT_BROWSER
}
def envLang = System.getProperty("geb.env.lang")
//Default language
if (!correctLanguage(envLang)) {
envLang = DEFAULT_LANGUAGE
}
System.setProperty("geb.env.lang", envLang)
System.setProperty("geb.env", browser)
environments {
driver = { getDriver(browser, envLang) }
}
Below is my spec file where I am trying to get the language value in a variable.
#Stepwise
class TC001_SMO_Scenario_Spec extends GebReportingSpec {
#Shared
def lang = System.getProperty("geb.env.lang")
def "Step 1: Perform Login"() {
when: "Load File"
to WUPage
then: " File loaded successfully"
println " Getting data from Geb Config File: " + lang
}
}
Can you please help me how to do this, as this is very important for me to access and store it in a variable. Thanks
The problem is your #Shared variable. The Geb manual says:
(...) declare a #Shared field. Again it’s best to initialize the field right at the point of declaration. (Semantically, this is equivalent to initializing the field at the very beginning of the setupSpec() method.)
The thing is, setupSpec() runs before GebConfig is evaluated. You can see it if you add this to the end of your GebConfig:
println "Finished evaluating GebConfig"
Then run this Geb specification (I have wrapped the variable assignments into closures and added print statements, then I am evaluating the closures so as to make the assignments work):
package de.scrum_master.stackoverflow
import geb.spock.GebReportingSpec
import spock.lang.Shared
class GebConfigIT extends GebReportingSpec {
#Shared
def sharedLang = {
println "Initialising sharedLang"
System.getProperty("geb.env.lang")
}()
def normalLang = {
println "Initialising normalLang"
System.getProperty("geb.env.lang")
}()
def setup() {
println "sharedLang = $sharedLang"
println "normalLang = $normalLang"
}
def foo() {
expect:
!sharedLang
normalLang
}
def bar() {
expect:
!sharedLang
normalLang
}
}
Initialising sharedLang
Finished evaluating GebConfig
Initialising normalLang
sharedLang = null
normalLang = nl
Initialising normalLang
Finished evaluating GebConfig
sharedLang = null
normalLang = nl
Can you see how sharedLang is initialised only once at the very beginning, before GebConfig even gets a chance to kick in?
Bottom line: Just remove #Shared from your code. It is over-used by most people anyway, they think they save time this way for cheap resources and tamper with their clean fixture setups. #Shared really is only for very expensive resources and very exceptional cases.

AST Transformation to wrap entire method body in a closure

I'm trying to do something rather simple. I would like to wrap the whole method code into an additional closure block that would measure the execution time. Right now I'm getting a really not helpful error message:
Error:Groovyc: NPE while processing Test.groovy
Annotation:
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.METHOD])
#GroovyASTTransformationClass(["WithTimingASTTransformation"])
public #interface WithTiming {
}
My wrapping closure:
class Benchmark {
static def measureTime(Closure cl) {
def start = System.currentTimeMillis()
def result = cl()
def time = System.currentTimeMillis() - start
println "it took $time"
result
}
}
My Transformation:
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class WithTimingASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
MethodNode method = astNodes[1]
method.code = wrap(method)
}
private Statement wrap(MethodNode method) {
def newBlock = new BlockStatement()
newBlock.addStatement(
new ExpressionStatement(
new StaticMethodCallExpression(
new ClassNode(Benchmark),
'measureTime',
new ArgumentListExpression(
new ClosureExpression(new Parameter[0], method.code)
))))
newBlock
}
}
I'm really stuck here and don't know how can I debug the problem.
There is an answer on a similar topic (wrapping whole method body into a try/catch block here). This works fine but my case is slightly different.
In my case similar NPE was coming from:
java.lang.NullPointerException
at org.codehaus.groovy.classgen.asm.ClosureWriter.createClosureClass(ClosureWriter.java:194)
at org.codehaus.groovy.classgen.asm.ClosureWriter.getOrAddClosureClass(ClosureWriter.java:159)
at org.codehaus.groovy.classgen.asm.ClosureWriter.writeClosure(ClosureWriter.java:90)
at org.codehaus.groovy.classgen.AsmClassGenerator.visitClosureExpression(AsmClassGenerator.java:673)
Whereas:
if (parameters == null || expression.getVariableScope() == null) {
parameters = Parameter.EMPTY_ARRAY;
} else if (parameters.length == 0) {
// let's create a default 'it' parameter
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
parameters = new Parameter[]{it};
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
if (ref != null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
}
and line 194 (as of https://github.com/groovy/groovy-core/commit/a52d0d3c5dd1cbb342992d36235171718a563c8b) is:
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
Thus you need to define a VariableScope for your ClosureExpression. I had to add tracing into org.codehaus.groovy.ast.ClosureWriter to find this, because there is an issue with exception display on stage of Class Generation - both in IntelliJ Idea and in Groovy Console - it does not show proper lines of code.
Furthermore, I think that either ClosureWriter or ClosureExpression constructor can be fixed to work aligned by default - without this NPE. I will possibly submit an issue to Groovy Jira for this.
Now I am able to inject closure expression in my code. But struggling to call this closure.
Getting:
groovy.lang.MissingMethodException: No signature of method: com.a9ae0b01f0ffc.VSMSGEN.implementation.T_visa_recon_generator$_convert_vts_log_to_ctf_closure2.call() is applicable for argument types: () values: []
Long story short, after some iterations my method-wrapping AST looks like this:
BlockStatement bs = new BlockStatement()
ClosureExpression closureExp = new ClosureExpression( methodNode.parameters, methodNode.code )
closureExp.variableScope = new VariableScope() // <- this does the trick!
bs.addStatement new ExpressionStatement( new StaticMethodCallExpression( new ClassNode( TransactionUtil ), 'wrap', new ArgumentListExpression( closureExp ) ) )
methodNode.code = bs
The line closureExp.variableScope = new VariableScope() avoids the NPE in ClosureWriter.java:194 and the whole thing runs like a charm!
Hope it helps someone...

Resources