groovy print environments from groovy.config - groovy

how do I print available environments from a config file? What is the form of the ojbect ConfigSlurper creates?
I tried
def config2 = new ConfigSlurper().parse(new File('obieeadmincfg.groovy').toURL())
config2.config.environments.each { println ${it} }
and
println prettyPrint(toJson(config2))
and
for ( i in 0 ..config2.config.environments.size()-1)
println config2.config.environments[i]
groovy.config
//Config3.groovy
obieadmin {
//default values
serverurl = "http://default.mycompany.com"
}
environments {
pldev01 {
obieeadmin {
serverurl = 'devgdwobi03.x.com'
}
}
plsbx02 {
obieeadmin {
serverurl = 'devgdwobi03.x.com'
}
}
}

I'm afraid you can't do this out-of box.
But using a bit of Groovy Metaprogramming it's achievable.
Groovy config slurper parses proper Groovy file, and you can do the same with GroovyShell. You can catch call to environment method providing closure in binding. In that closure you have to collect all top-level method calls(with same methodMissing).
Providing base script with property and method missing handlers, you can suppress runtime errors, and execute script without much care to other properties.
Not the nicest path, but it works.
package test
import org.codehaus.groovy.control.CompilerConfiguration
class A extends Script {
def propertyMissing(String name) { null }
def propertyMissing(String name, def arg) {}
def methodMissing(String name, def args) {}
#Override Object run() { null }
}
class NameCollector {
def envs = []
def methodMissing(String name, def args) { envs << name }
}
// configure interceptors.
def configuration = new CompilerConfiguration()
configuration.scriptBaseClass = 'test.A'
def nc = new NameCollector()
def environments = { Closure it ->
it.delegate = nc;
it.resolveStrategy = Closure.DELEGATE_ONLY
it()
}
// execute config script
new GroovyShell([environments: environments] as Binding, configuration).evaluate(new File("config.groovy"))
nc.envs // Return, print them.

Not sure if this is going to work forever but currently you can do it by overriding the setting for the 'environments' conditional block, like this:
def config = """environments {
dev {
foo = "bar"
}
prod {
foo = "baz"
}
}"""
def configSlurper = new ConfigSlurper()
configSlurper.registerConditionalBlock('environments', null)
assert configSlurper.parse(config).environments.keySet() == ['dev', 'prod'].toSet()

Related

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'

Passing different parameters to created jobs

On Jenkins, using Job DLS plugin, I'm trying to prepare one script which will create jobs configured for different environments (dev and preprod). Depends on for which environments this job has to run, different parameters are needed.
In this situation how to define, in the shortest way, that parameters for dev environment include the same as preprod parameters plus additionally i.e. 2 more?
An example of the code which I use is presented below.
def environments = ["DEV", "PREPROD"]
def names = ["name1", "name2", "name3"]
def jobParameters = {
string {
name("browser")
defaultValue("CHROME")
description("Browser on which one tests will run")
trim(true)
}
string {
name("parameter1")
defaultValue("")
description("")
trim(true)
}
}
def jobParametersDev = {
jobParameters
string {
name("parameter2")
defaultValue("")
description("")
trim(true)
}
string {
name("parameter3")
defaultValue("")
description("")
trim(true)
}
}
def createJob(name, env, runCommand, jobParameters) {
job("Job-${-> name}-${-> env}") {
description("My first job for ${-> name}")
parameters(jobParameters)
steps {
shell {
command(runCommand)
}
}
}
}
for (name in names) {
for (env in environments) {
if (env == 'DEV') {
def runCommand = "python35 -u ./TestSuite-${-> name}.py %parameter1% %parameter2%,%parameter3% %browser%"
createJob(name, env, runCommand, jobParametersDev)
} else {
def runCommand = "python35 -u ./TestSuite-${-> name}.py %parameter1% ${-> env} %browser%"
createJob(name, env, runCommand, jobParameters)
}
}
}
To summarise - the last thing which I tried is:
def jobParametersDev = {
jobParameters
...
}
But it doesn't work... Only values for jobParametersDev are visible.
How to add these values? If it's not necessary I don't want to double the same code.
I will be really grateful for any help.
You can not simply call one closure within another. But you can chain method calls. You just need to pass the job reference.
def generateParameters = { job ->
job.parameters {
stringParam('param3', '', '')
// more params here...
}
}
def generateDevParameters = { job ->
generateParameters(job)
job.parameters {
stringParam('param4', '', '')
// more params here...
}
}
def createJob(name, generateParameters) {
def j = job(name) {
// more config here...
}
generateParameters(j)
}
createJob('test1', generateParameters)
createJob('test2', generateDevParameters)

Groovy map constructor keys to different variable names

I have JSON looking like:
{
"days": [
{
"mintemp": "21.8"
}
]
}
With Groovy, I parse it like this:
class WeatherRow {
String mintemp
}
def file = new File("data.json")
def slurper = new JsonSlurper().parse(file)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.mintemp
But actually, I would like to name my instance variable something like minTemp (or even something completely random, like numberOfPonies). Is there a way in Groovy to map a member of a map passed to a constructor to something else?
To clarify, I was looking for something along the lines of #XmlElement(name="mintemp"), but could not easily find it:
class WeatherRow {
#Element(name="mintemp")
String minTemp
}
Create a constructor that takes a map.
Runnable example:
import groovy.json.JsonSlurper
def testJsonStr = '''
{"days": [
{ "mintemp": "21.8" }
]}'''
class WeatherRow {
String minTemp
WeatherRow(map) {
println "Got called with constructor that takes a map: $map"
minTemp = map.mintemp
}
}
def slurper = new JsonSlurper().parseText(testJsonStr)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.minTemp
Result:
Got called with constructor that takes a map: [mintemp:21.8]
21.8
(of course you'd remove the println line, it's just there for the demo)
You can achieve this using annotation and simple custom annotation processor like this:
1. Create a Custom Annotation Class
#Retention(RetentionPolicy.RUNTIME)
#interface JsonDeserializer {
String[] names() default []
}
2. Annotate your instance fields with the custom annotation
class WeatherRow{
#JsonDeserializer(names = ["mintemp"])
String mintemp;
#JsonDeserializer(names = ["mintemp"])
String minTemp;
#JsonDeserializer(names = ["mintemp"])
String numberOfPonies;
}
3. Add custom json deserializer method using annotation processing:
static WeatherRow fromJson(def jsonObject){
WeatherRow weatherRow = new WeatherRow();
try{
weatherRow = new WeatherRow(jsonObject);
}catch(MissingPropertyException ex){
//swallow missing property exception.
}
WeatherRow.class.getDeclaredFields().each{
def jsonDeserializer = it.getDeclaredAnnotations()?.find{it.annotationType() == JsonDeserializer}
def fieldNames = [];
fieldNames << it.name;
if(jsonDeserializer){
fieldNames.addAll(jsonDeserializer.names());
fieldNames.each{i ->
if(jsonObject."$i")//TODO: if field type is not String type custom parsing here.
weatherRow."${it.name}" = jsonObject."$i";
}
}
};
return weatherRow;
}
Example:
def testJsonStr = '''
{
"days": [
{
"mintemp": "21.8"
}
]
}'''
def parsedWeatherRows = new JsonSlurper().parseText(testJsonStr);
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).mintemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).minTemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).numberOfPonies == "21.8"
Check the full working code at groovyConsole.

Groovy DSL: How can I let two delegating classes handle different parts of a DSLScript?

Let's say I have a DSL like this
setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "I'm doing dsl stuff"}
One would have a delegating class implementing the methods 'setup' and 'doStuff' usually. Beside, one could write common Groovy code to be executed (println...).
What I am searching for, is a way to execute this in two steps. In the first step only the setup method should be processed (neither println). The second step handles the other parts.
At the moment, I have two delegating classes. One implements 'setup' the other one implements 'doStuff'. But both execute the println statement, of course.
You can create a single class to intercept the method calls from the script and let it coordinate the following method invoke. I did it through reflection, but you can go declarative if you want. These are the model and script classes:
class FirstDelegate {
def setup(closure) { "firstDelegate.setup" }
}
class SecondDelegate {
def doStuff(closure) { "secondDelegate.doStuff" }
}
class MethodInterceptor {
def invokedMethods = []
def methodMissing(String method, args) {
invokedMethods << [method: method, args: args]
}
def delegate() {
def lookupCalls = { instance ->
def invokes = instance.metaClass.methods.findResults { method ->
invokedMethods.findResult { invocation ->
invocation.method == method.name ?
[method: method, invocation: invocation] : null
}
}
invokes.collect { invoked ->
invoked.method.invoke(instance, invoked.invocation.args)
}
}
return lookupCalls(new FirstDelegate()) + lookupCalls(new SecondDelegate())
}
}
Here be scripts and assertions:
import org.codehaus.groovy.control.CompilerConfiguration
def dsl = '''
setup {name = "aDSLScript"}
println "this is common groovy code"
doStuff {println "Ima doing dsl stuff"}
'''
def compiler = new CompilerConfiguration()
compiler.scriptBaseClass = DelegatingScript.class.name
def shell = new GroovyShell(this.class.classLoader, new Binding(), compiler)
script = shell.parse dsl
interceptor = new MethodInterceptor()
script.setDelegate interceptor
script.run()
assert interceptor.invokedMethods*.method == [ 'setup', 'doStuff' ]
assert interceptor.delegate() ==
['firstDelegate.setup', 'secondDelegate.doStuff']
Notice I didn't bothered intercepting println call, which is a DefaultGroovyMethods thus, a little more cumbersome to handle.
Also having the class MethodInterceptor implementing the method delegate() is not a good idea, since this allows the user-defined script to call it.
I found a way to split up execution of the DSL script. I used a CompilationCustomizer to remove every statement from AST except the doFirst{}. So the first run will only execute doFirst. The second run does everything else. Here's some code:
class DoFirstProcessor {
def doFirst(Closure c) {
c()
}
}
class TheRestProcessor {
def doStuff(Closure c) {
c()
}
def methodMissing(String name, args) {
//nothing to do
}
}
def dsl = "
println 'this is text that will not be printed out in first line!'
doFirst { println 'First things first: e.g. setting up environment' }
doStuff { println 'doing some stuff now' }
println 'That is it!'
"
class HighlanderCustomizer extends CompilationCustomizer {
def methodName
HighlanderCustomizer(def methodName) {
super(CompilePhase.SEMANTIC_ANALYSIS)
this.methodName = methodName
}
#Override
void call(SourceUnit sourceUnit, GeneratorContext generatorContext, ClassNode classNode) throws CompilationFailedException {
def methods = classNode.getMethods()
methods.each { MethodNode m ->
m.code.each { Statement st ->
if (!(st instanceof BlockStatement)) {
return
}
def removeStmts = []
st.statements.each { Statement bst ->
if (bst instanceof ExpressionStatement) {
def ex = bst.expression
if (ex instanceof MethodCallExpression) {
if (!ex.methodAsString.equals(methodName)) {
removeStmts << bst
}
} else {
removeStmts << bst
}
} else {
removeStmts << bst
}
}
st.statements.removeAll(removeStmts)
}
}
}
}
def cc = new CompilerConfiguration()
cc.addCompilationCustomizers new HighlanderCustomizer("doFirst")
cc.scriptBaseClass = DelegatingScript.class.name
def doFirstShell = new GroovyShell(new Binding(), cc)
def doFirstScript = doFirstShell.parse dsl
doFirstScript.setDelegate new DoFirstProcessor()
doFirstScript.run()
cc.compilationCustomizers.clear()
def shell = new GroovyShell(new Binding(), cc)
def script = shell.parse dsl
script.setDelegate new TheRestProcessor()
script.run()
I did another variation of this where I execute the DSL in one step. See my blog post about it: http://hackserei.metacode.de/?p=247

Groovy: Implicit call not working on instance variables inside closure

A class implements call method so that it's objects can be called as a method. This works for most of the case but not when the call is being made inside a closure on a object which is instance variable of a class.
To demonstrate the problem, in the code below I've commented the interesting lines with numbers. While most variants result in same output, only the line with comment 5 doesn't work. It throws groovy.lang.MissingMethodException: No signature of method: Client2.instanceVar() is applicable for argument types: () values: [])
Can someone help me understand the reason? Is it a bug?
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { instanceVar() } // doesn't work
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
I guess this will make it clear.
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def getInstanceVar() {
println "Getter Called"
instanceVar
}
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { this.#instanceVar() } //should work now
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
You will see "Getter Called" printed when closure2() invoked. For a global property to be accessed in the closure inside a method, the getter in called instead. To surmount the error you get, the field instanceVar needs to be accessed directly in order to implicitly use call().

Resources