External Content with Groovy BuilderSupport - groovy

I've built a custom builder in Groovy by extending BuilderSupport. It works well when configured like nearly every builder code sample out there:
def builder = new MyBuilder()
builder.foo {
"Some Entry" (property1:value1, property2: value2)
}
This, of course, works perfectly. The problem is that I don't want the information I'm building to be in the code. I want to have this information in a file somewhere that is read in and built into objects by the builder. I cannot figure out how to do this.
I can't even make this work by moving the simple entry around in the code.
This works:
def textClosure = { "Some Entry" (property1:value1, property2: value2) }
builder.foo(textClosure)
because textClosure is a closure.
If I do this:
def text = '"Some Entry" (property1:value1, property2: value2)'
def textClosure = { text }
builder.foo(textClosure)
the builder only gets called for the "foo" node. I've tried many variants of this, including passing the text block directly into the builder without wrapping it in a closure. They all yield the same result.
Is there some way I take a piece of arbitrary text and pass it into my builder so that it will be able to correctly parse and build it?

Your problem is that a String is not Groovy code. The way ConfigSlurper handles this is to compile the text into an instance of Script using GroovyClassLoader#parseClass. e.g.,
// create a Binding subclass that delegates to the builder
class MyBinding extends Binding {
def builder
Object getVariable(String name) {
return { Object... args -> builder.invokeMethod(name,args) }
}
}
// parse the script and run it against the builder
new File("foo.groovy").withInputStream { input ->
Script s = new GroovyClassLoader().parseClass(input).newInstance()
s.binding = new MyBinding(builder:builder)
s.run()
}
The subclass of Binding simply returns a closure for all variables that delegates the call to the builder. So assuming foo.groovy contains:
foo {
"Some Entry" (property1:value1, property2: value2)
}
It would be equivalent to your code above.

I think the problem you described is better solved with a slurper or parser.
See:
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlParser
for XML based examples.
In your case. Given the XML file:
<foo>
<entry name='Some Entry' property1="value1" property2="value2"/>
</foo>
You could slurp it with:
def text = new File("test.xml").text
def foo = new XmlSlurper().parseText(text)
def allEntries = foo.entry
allEntries.each {
println it.#name
println it.#property1
println it.#property2
}

Originally, I wanted to be able to specify
"Some Entry" (property1:value1, property2: value2)
in an external file. I'm specifically trying to avoid XML and XML-like syntax to make these files easier for regular users to create and modify. My current solution uses ConfigSlurper and the file now looks like:
"Some Entry"
{
property1 = value1
property2 = value2
}
ConfigSlurper gives me a map like this:
["Some Entry":[property1:value1,property2:value2]]
It's pretty simple to use these values to create my objects, especially since I can just pass the property/value map into the constructor.

Related

How to traverse AST tree

I'm trying to create an static analysis for Groovy. As a POC for my superiors I'm just trying to parse simple code and detect SQL injections, which are the easiest kind to spot. I did it successfully on Python, which is my main language, but my company mostly uses Grails (on Groovy).
This is what I have so far:
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.ast.CodeVisitorSupport
import org.codehaus.groovy.ast.builder.AstBuilder
public class SecurityCheck extends CodeVisitorSupport {
void visitBlockStatement(BlockStatement statement) {
println "NEW BLOCK STATEMENT:"
println statement.getText();
//keep walking...
statement.getStatements().each { ASTNode child ->
println "CHILD FOUND: "
println child.getText();
child.visit(this)
}
}
}
def code = new File('groovy_source.groovy').text // get the code from the source file
def AstBuilder astBuilder = new AstBuilder() // build an instance of the ast builder
def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, code) // build from string when the compiler converts from tokens to AST
def SecurityCheck securityCheck = new SecurityCheck() // create an instance of our security check class
println ast
println ast[0]
ast[0].visit(securityCheck)
The groovy_source.groovy file is very simple, containing only a minimal file with a super easy to spot vulnerability:
def post(id) {
query = "SELECT * FROM table WHERE id = " + id;
result = sql.execute query
return result;
}
It is my understanding that, as I'm inheriting from CodeVisitorSupport, this would just visit a BlockStatement and then, for each statement inside that statement, it would visit it using the method from the supper class.
Nevertheless, when I print the text from the BlockStatement, it is an empty string, and the for each method never even gets called (which I assume must mean the AST found no children for my block statement, even when the function definitively has statements inside it.
[org.codehaus.groovy.ast.stmt.BlockStatement#363a52f[]] // println ast
org.codehaus.groovy.ast.stmt.BlockStatement#363a52f[] // println ast[0]
NEW BLOCK STATEMENT:
{ } // println statement.getText()
Any help here would be tremendously appreciated. Thanks!
I found the answer. I wasn't so hard in the end, but the horrible documentation doesn't make it easy. If you one to traverse the tree, you need to give the constructor the false boolean as a second argument, like this:
def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, false, code)
Then you can use the visit* methods as you expect.

Groovy - Type check AST generated code

I have a Groovy application that can be custimized by a small Groovy DSL I wrote. On startup, the application loads several Groovy scripts, applies some AST transformations and finally executes whatever was specified in the scripts.
One of the AST transformations inserts a couple of lines of code into certain methods. That works fine and I can see the different behavior during runtime. However, sometimes the generated code is not correct. Although I load the scripts with the TypeChecked customizer in place, my generated code is never checked for soundness.
To show my problem, I constructed an extreme example. I have the following script:
int test = 10
println test // prints 10 when executed without AST
I load this script and insert a new line of code between the declaration of test and println:
public void visitBlockStatement(BlockStatement block) {
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant 15
}
}).first()
def newStmt = new ExpressionStatement(assignment)
newStmt.setSourcePosition(block.statements[1])
block.statements.add(2, newStmt)
super.visitBlockStatement(block)
}
After applying this AST, the script prints 15. When I use AstNodeToScriptVisitor to print the Groovy code of the resulting script, I can see the new assignment added to the code.
However, if I change the value of the assignment to a String value:
// ...
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant "some value"
}
}).first()
// ...
I get a GroovyCastExcpetion at runtime. Although the resulting script looks like this:
int test = 10
test = "some value" // no compile error but a GroovyCastException at runtime here. WHY?
println test
no error is raised by TypeChecked. I read in this mailing list, that you need to set the source position for generated code to be checked, but I'm doing that an it still doesn't work. Can anyone provide some feedback of what I am doing wrong? Thank you very much!
Update
I call the AST by attaching it to the GroovyShell like this:
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ASTTransformationCustomizer(TypeChecked)
)
config.addCompilationCustomizers(
new ASTTransformationCustomizer(new AddAssignmentAST())
)
def shell = new GroovyShell(config)
shell.evaluate(new File("./path/to/file.groovy"))
The class for the AST itself looks like this:
#GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class AddAssignmentAST implements ASTTransformation {
#Override
public void visit(ASTNode[] nodes, SourceUnit source) {
def transformer = new AddAssignmentTransformer()
source.getAST().getStatementBlock().visit(transformer)
}
private class AddAssignmentTransformer extends CodeVisitorSupport {
#Override
public void visitBlockStatement(BlockStatement block) {
// already posted above
}
}
}
Since my Groovy script only consists of one block (for this small example) the visitBlockStatement method is called exactly once, adds the assignment (which I can verify since the output changes) but does not ever throw a compile-time error.

Scope of Groovy's metaClass?

I have an application which can run scripts to automate certain tasks. I'd like to use meta programming in these scripts to optimize code size and readability. So instead of:
try {
def res = factory.allocate();
... do something with res ...
} finally {
res.close()
}
I'd like to
Factory.metaClass.withResource = { c ->
try {
def res = factory.allocate();
c(res)
} finally {
res.close()
}
}
so the script writers can write:
factory.withResource { res ->
... do something with res ...
}
(and I could do proper error handling, etc).
Now I wonder when and how I can/should implement this. I could attach the manipulation of the meta class in a header which I prepend to every script but I'm worried what would happen if two users ran the script at the same time (concurrent access to the meta class).
What is the scope of the meta class? The compiler? The script environment? The Java VM? The classloader which loaded Groovy?
My reasoning is that if Groovy meta classes have VM scope, then I could run a setup script once during startup.
Metaclasses exist per classloader [citation needed]:
File /tmp/Qwop.groovy:
class Qwop { }
File /tmp/Loader.groovy:
Qwop.metaClass.bar = { }
qwop1 = new Qwop()
assert qwop1.respondsTo('bar')
loader = new GroovyClassLoader()
clazz = loader.parseClass new File("/tmp/Qwop.groovy")
clazz.metaClass.brap = { 'oh my' }
qwop = clazz.newInstance()
assert !qwop.respondsTo('bar')
assert qwop1.respondsTo('bar')
assert qwop.brap() == "oh my"
assert !qwop1.respondsTo('brap')
// here be custom classloaders
new GroovyShell(loader).evaluate new File('/tmp/QwopTest.groovy')
And a script to test the scoped metaclass (/tmp/QwopTest.groovy):
assert !new Qwop().respondsTo('bar')
assert new Qwop().respondsTo('brap')
Execution:
$ groovy Loaders.groovy
$
If you have a set of well defined classes you could apply metaprogramming on top of the classes loaded by your classloader, as per the brap method added.
Another option for this sort of thing which is better for a lot of scenarios is to use an extension module.
package demo
class FactoryExtension {
static withResource(Factory instance, Closure c) {
def res
try {
res = instance.allocate()
c(res)
} finally {
res?.close()
}
}
}
Compile that and put it in a jar file which contains a file at META-INF/services/org.codehaus.groovy.runtime.ExtensionModule that contains something like this...
moduleName=factory-extension-module
moduleVersion=1.0
extensionClasses=demo.FactoryExtension
Then in order for someone to use your extension they just need to put that jar file on their CLASSPATH. With all of that in place, a user could do something like this...
factoryInstance.withResource { res ->
... do something with res ...
}
More information on extension modules is available at http://docs.groovy-lang.org/docs/groovy-2.3.6/html/documentation/#_extension_modules.

Groovy MarkupBuilder name conflict

I have this code:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
'identity'() {
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}
}
return writer.toString();
}
It produces this xml:
<catalog xmlns='http://www.sybrium.com/XMLSchema/NodeCatalog'>
<groupId>sample.group</groupId>
<artifactId>sample-artifact</artifactId>
<version>1.0.0</version>
</catalog>
Notice that the "identity" tag is missing... I've tried everything in the world to get that node to appear. I'm ripping my hair out!
Thanks in advance.
There might be a better way, but one trick is to call invokeMethod directly:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
delegate.invokeMethod('identity', [{
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}])
}
return writer.toString();
}
This is effectively what Groovy is doing behind the scenes. I couldn't get delegate.identity or owner.identity to work, which are the usual tricks.
Edit: I figured out what's going on.
Groovy adds a method with a signature of identity(Closure c) to every object.
This means that when you tried to dynamically invoke the identity element on the XML builder, while passing in a single closure argument, it was calling the identity() method, which is like calling delegate({...}) on the outer closure.
Using the invokeMethod trick forces Groovy to bypass the Meta Object Protocol and treat the method as a dynamic method, even though the identity method already exists on the MetaObject.
Knowing this, we can put together a better, more legible solution. All we have to do is change the signature of the method, like so:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
// NOTE: LEAVE the empty map here to prevent calling the identity method!
identity([:]) {
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}
}
return writer.toString();
}
This is much more readable, it's clearer the intent, and the comment should (hopefully) prevent anyone from removing the "unnecessary" empty map.

how to detect caller instance in SoapUI groovy script?

A SoapUI project can run random script upon load.
Load Script is invoked with log and project variables.
In my shared lib I have method - addAsserts() that traverses the whole project and adds schema compliance assertions to SOAP test steps. In my Load Script I call shared method
addAsserts(this)
passing 'this' as a parameter and set closure.delegate to it inside addAsserts method to make 'project' variable accessible within the closure scope
addAsserts method is defined in sharedUtil.groovy:
static def addAsserts(that){
def closure={
project.testSuites.each { testSuiteName, testSuiteObject ->
testSuiteObject.testCases.each { testCaseName, testCaseObject ->
testCaseObject.testSteps.each { testStepName, testStepObject ->
if ("class com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep" == testStepObject.getClass().toString() ) {
log.info "adding 'Schema Compliance' assertion to ${testSuiteName}/${testCaseName}/${testStepName}"
testStepObject.addAssertion('Schema Compliance')
}
}
}
}
}//closure
closure.delegate=that // <--- i would like NOT to pass 'that' as parameter
// but rather detect in runtime with some kind of
// getCallerInstance() method
return closure.call()
}
QUESTION:
Is it possible to detect caller instance in runtime with some kind of getCallerInstance() method ?
No, I don't believe this is possible. Wasn't in Java either (you can find out the name/method of the calling class using some horrible stacktrace hacking, but not the instance of the class itself)
Edit...
It might be possible with a Category (but I am not experienced with SoapUI, so I don't know if this technique would fit)
Say we have a class Example defined like so:
class Example {
String name
}
We can then write a class very similar to your example code, which in this case will set the delegate of the closure, and the closure will print out the name property of the delegate (as we have set the resolve strategy to DELEGATE_ONLY)
class AssetAddingCategory {
static def addAsserts( that ) {
def closure = {
"Name of object: $name"
}
closure.delegate = that
closure.resolveStrategy = Closure.DELEGATE_ONLY
closure.call()
}
}
Later on in our code, it is then possible to do:
def tim = new Example( name:'tim' )
use( AssetAddingCategory ) {
println tim.addAsserts()
}
And this will print out
Name of object: tim

Resources