Groovy: strange error with dynamic method invocation - dynamic

The following is a program that dynamically calls all the getXXX methods on an object of CLASS, where the CLASS-name gets passed via command-line. And it works just fine.
// Program: callAllMethods.groovy
// Invoke this program as: groovy callAllMethods Date
args.each { arg ->
println "Methods of ${arg} ..."
def code = """
x = new ${arg}()
x.class.methods.each { f ->
if (f.name.startsWith("get")) {
print "new ${arg}()." + f.name + ": " + f.invoke(x)
println ''
}
}
"""
evaluate("$code")
println ''
}
However, when I try the simpler style of dynamic-method invocation (that does not use METHOD.invoke(OBJECT) but rather OBJECT."METHOD-NAME"()), like so,
// Program: callAllMethods.groovy
// Invoke this program as: groovy callAllMethods Date
args.each { arg ->
println "Methods of ${arg} ..."
def code = """
x = new ${arg}()
x.class.methods.each { f ->
if (f.name.startsWith("get")) {
result = x."${f.name}"()
println "new ${arg}().${f.name}: ${result}"
}
}
"""
evaluate("$code")
println ''
}
... I get the following error:
$ groovy callGetMethods.groovy Date
Methods of Date ...
Caught: groovy.lang.MissingPropertyException: No such property: f for class: callGetMethods
groovy.lang.MissingPropertyException: No such property: f for class: callGetMethods
at callGetMethods$_run_closure1.doCall(callGetMethods.groovy:13)
at callGetMethods.run(callGetMethods.groovy:10)
I cannot understand why! Groovy version that I'm using:
$ groovy -version
Groovy Version: 2.1.3 JVM: 1.6.0_43 Vendor: Sun Microsystems Inc. OS: Linux

It happens because when you use the reflection based one (the x.class.methods.each one), you concatenate and generate the code upon GString evaluation, which resolves only one variable against the current scope, which is arg, and that is ok. If you print the code, it outputs a perfectly runnable Groovy code:
x = new Date()
x.class.methods.each { f ->
if (f.name.startsWith("get")) {
print "new Date()." + f.name + ": " + f.invoke(x)
println ''
}
}
In your second version, the GString variables are resolved against the scope they were created, which is the script binding. So it tries to fetch the f variable from that scope, and not from the code variable. And that is why it crashes at the ${f} variable.
If you change the code variable into a plain string (single quotes), it won't resolve the variable arg, and thus you will need to tweak a bit around it to create a new class from it. Even so, it will fail, unless you pass as argument groovy callAllMethods java.util.Date, which is not groovy (pun intended).
So, to use your code in that way, the GString shouldn't be resolved in declaration time, but rather at evaluate() time. Still, the arg variable needs to be resolved in declaration time, thus, you need to concat it. Here is the result:
args.each { arg ->
println "Methods of ${arg} ..."
def code = '''
x = new '''+arg+'''()
x.class.methods.each { m ->
if (m.name.startsWith("get")) {
result = x."${m.name}"()
println "new '''+arg+'''().${m.name}: ${result}"
}
}
'''
evaluate code
println ''
}
Which, in my box (jdk7, groovy 2.1.3), outputs:
new Date().getDay: 0
new Date().getTimezoneOffset: 180
new Date().getDate: 2
new Date().getHours: 10
new Date().getMinutes: 39
new Date().getMonth: 5
new Date().getSeconds: 56
new Date().getTime: 1370180396136
new Date().getYear: 113
new Date().getClass: class java.util.Date
If you just want to output the properties from an object, may i suggest the object.properties?
args.each { arg ->
println "Methods of ${arg} ..."
def code = '''
x = new '''+arg+'''()
x.properties.each {
println "new '''+arg+'''().${it.key}: ${x[it.key]}"
}
'''
evaluate code
println ''
}
It outputs a bit more stuff for Date, though :-).

Related

How to duplicate output of process to file and console (in Kotlin program)

I have a simple snippet of Kotlin code
import java.io.*
import java.util.concurrent.TimeUnit.MINUTES
import java.util.concurrent.TimeUnit
val proc = ProcessBuilder("C:\\tools\\build\\maven\\3.6.1\\bin\\mvn.cmd")
.directory(null)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
println("Started")
proc.waitFor(60, TimeUnit.MINUTES)
println("Ended")
val output : String = proc.inputStream.bufferedReader().readText()
println("Output : " + output)
var log : File = File("cmd.log")
log.writeText(output)
This code run command then saves output in string then print output to console
But printing is occured when external program is completed.
My question is:
I would like to see program output in real time mode.
From other hands I would like to see output of external program while it works.
This code run as Kotlin script : kotlinc -script script.ktc
If place string
val output : String = proc.inputStream.bufferedReader().readText()
Before waitFor call. I observed that execution is blocked as expected
I'm sorry for this question, it was quite simple,I found simple answer today.
val proc = ProcessBuilder("ls", "-lR", "c:\\toolchains")
.directory(null)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
println("Started")
val output: BufferedReader = proc.inputStream.bufferedReader()
var line: String? = output.readLine()
val log: File = File("cmd.log")
while (line != null) {
println("Next Line " + line)
log.appendText(line.toString() + System.lineSeparator())
line = output.readLine()
}
proc.waitFor(60, TimeUnit.MINUTES)
println("Ended")

Why I receive an error when I use exposed in Kotlin?

I need to receive region_name by region_code from Oracle DB
I use Exposed for my program, but I receive error
in thread "main" java.lang.AbstractMethodError
at org.jetbrains.exposed.sql.Transaction.closeExecutedStatements(Transaction.kt:181)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:137)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:75)
Code is
object Codes : Table("REGIONS") {
val region_code = varchar("region_code",32)
val region_name = varchar("region_name",32)}
The main fun contains
.......
val conn = Database.connect("jdbc:oracle:thin:#//...", driver = "oracle.jdbc.OracleDriver",
user = "...", password = "...")
transaction(java.sql.Connection.TRANSACTION_READ_COMMITTED, 1, conn) {
addLogger(StdOutSqlLogger)
Codes.select { Codes.region_code eq "a" }.limit(1).forEach {
print(it[Codes.region_name])
}
}
AbstractMethodError usually means that you compiled the code with one version of a library, but are running it against a different (incompatible) version.  (See for example these questions.)
So I'd check your dependencies &c carefully.

Test file structure in groovy(Spock)

How to test created and expected file tree in groovy(Spock)?
Right now I'm using Set where I specify paths which I expect to get and collecting actual paths in this way:
Set<String> getCreatedFilePaths(String root) {
Set<String> createFilePaths = new HashSet<>()
new File(root).eachFileRecurse {
createFilePaths << it.absolutePath
}
return createFilePaths
}
But the readability of the test isn't so good.
Is it possible in groovy to write expected paths as a tree, and after that compare with actual
For example, expected:
region:
usa:
new_york.json
california.json
europe:
spain.json
italy.json
And actual will be converted to this kind of tree.
Not sure if you can do it with the built-in recursive methods. There certainly are powerful ones, but this is standard recursion code you can use:
def path = new File("/Users/me/Downloads")
def printTree(File file, Integer level) {
println " " * level + "${file.name}:"
file.eachFile {
println " " * (level + 1) + it.name
}
file.eachDir {
printTree(it, level + 1)
}
}
printTree(path, 1)
That prints the format you describe
You can either build your own parser or use Groovy's built-in JSON parser:
package de.scrum_master.stackoverflow
import groovy.json.JsonParserType
import groovy.json.JsonSlurper
import spock.lang.Specification
class FileRecursionTest extends Specification {
def jsonDirectoryTree = """{
com : {
na : {
tests : [
MyBaseIT.groovy
]
},
twg : {
sample : {
model : [
PrimeNumberCalculatorSpec.groovy
]
}
}
},
de : {
scrum_master : {
stackoverflow : [
AllowedPasswordsTest.groovy,
CarTest.groovy,
FileRecursionTest.groovy,
{
foo : [
LoginIT.groovy,
LoginModule.groovy,
LoginPage.groovy,
LoginValidationPage.groovy,
User.groovy
]
},
LuceneTest.groovy
],
testing : [
GebTestHelper.groovy,
RestartBrowserIT.groovy,
SampleGebIT.groovy
]
}
}
}"""
def "Parse directory tree JSON representation"() {
given:
def jsonSlurper = new JsonSlurper(type: JsonParserType.LAX)
def rootDirectory = jsonSlurper.parseText(jsonDirectoryTree)
expect:
rootDirectory.de.scrum_master.stackoverflow.contains("CarTest.groovy")
rootDirectory.com.twg.sample.model.contains("PrimeNumberCalculatorSpec.groovy")
when:
def fileList = objectGraphToFileList("src/test/groovy", rootDirectory)
fileList.each { println it }
then:
fileList.size() == 14
fileList.contains("src/test/groovy/de/scrum_master/stackoverflow/CarTest.groovy")
fileList.contains("src/test/groovy/com/twg/sample/model/PrimeNumberCalculatorSpec.groovy")
}
List<File> objectGraphToFileList(String directoryPath, Object directoryContent) {
List<File> files = []
directoryContent.each {
switch (it) {
case String:
files << directoryPath + "/" + it
break
case Map:
files += objectGraphToFileList(directoryPath, it)
break
case Map.Entry:
files += objectGraphToFileList(directoryPath + "/" + (it as Map.Entry).key, (it as Map.Entry).value)
break
default:
throw new IllegalArgumentException("unexpected directory content value $it")
}
}
files
}
}
Please note:
I used new JsonSlurper(type: JsonParserType.LAX) in order to avoid having to quote each single String in the JSON structure. If your file names contain spaces or other special characters, you will have to use something like "my file name", though.
In rootDirectory.de.scrum_master.stackoverflow.contains("CarTest.groovy") you can see how you can nicely interact with the parsed JSON object graph in .property syntax. You might like it or not, need it or not.
Recursive method objectGraphToFileList converts the parsed object graph to a list of files (if you prefer a set, change it, but File.eachFileRecurse(..) should not yield any duplicates, so the set is not needed.
If you do not like the parentheses etc. in the JSON, you can still build your own parser.
You might want to add another utility method to create a JSON string like the given one from a validated directory structure, so you have less work when writing similar tests.
Modified Bavo Bruylandt answer to collect file tree paths, and sort it to not care about the order of files.
def "check directory structure"() {
expect:
String created = getCreatedFilePaths(new File("/tmp/region"))
String expected = new File("expected.txt").text
created == expected
}
private String getCreatedFilePaths(File root) {
List paths = new ArrayList()
printTree(root, 0, paths)
return paths.join("\n")
}
private void printTree(File file, Integer level, List paths) {
paths << ("\t" * level + "${file.name}:")
file.listFiles().sort{it.name}.each {
if (it.isFile()) {
paths << ("\t" * (level + 1) + it.name)
}
if (it.isDirectory()) {
collectFileTree(it, level + 1, paths)
}
}
}
And expected files put in the expected.txt file with indent(\t) in this way:
region:
usa:
new_york.json
california.json
europe:
spain.json
italy.json

Can I pass inputs to gradle tasks?

Is it possible to pass inputs to a gradle task? For example, you can pass inputs to a java or a c program. Can I do something similar with a gradle task?
If you mean, that you need to pass some value via command line arguments, you can do it like:
customTask {
if ( project.hasProperty("someArg") ) {
println project.property("someArg")
}
}
Then run your application with this argument
gradle customTask -PsomeArg=PassedValue
Or you can make a task and get the values on execution phase, like:
def username
def password
task getCridentials << {
def console = System.console()
if (console) {
username = console.readLine('username: ')
password = console.readPassword('password: ')
} else {
logger.error "Can't get console."
}
}

Define a variable and set it to a default value if something goes wrong during definition

I have the following code in my build.gradle
Contents in version.properties are:
buildVersion=1.2.3
Value of $v variable during the Gradle build is coming as: 1.2.3
Value of $artifactoryVersion variable in JENKINS build is coming as: 1.2.3.1, 1.2.3.2, 1.2.3.x ... and so on where the 4th digit is Jenkins BUILD_NUMBER available to gradle build script during Jenkins build.
BUT, when I'm running this build.gradle on my desktop where I dont have BUILD_NUMBER variable available or set in my ENVIRONMENT variables, I get an error saying trim() can't work on null. (as there's no BUILD_NUMBER for Desktop/local build).
I'm trying to find a way i.e.
What should I code in my script so that if BUILD_NUMBER is not available, then instead of gradle build processing failing for an error, it'd set jenkinsBuild = "0" (hard coded) otherwise, pick what it gets during Jenkins build.
For ex: in Bash, we set a variable var1=${BUILD_NUMBER:-"0"} which will set var1 to a valid Jenkins BUILD number if it's available and set to a value, otherwise if it's NULL, then var1 = "0".
I DON'T want to have each developer/user set this BUILD_NUMBER in some property file. All I want is, if this variable doesn't exist, then the code should put "0" in jenkinsBuilds variable and doesn't error out during desktop builds. I know during Jenkins build, it's working fine.
// Build Script
def fname = new File( 'version.properties' )
Properties props = new Properties()
props.load( new FileInputStream( fname ) )
def v = props.get( 'buildVersion' )
def env = System.getenv()
def jenkinsBuild = env['BUILD_NUMBER'].trim()
if( jenkinsBuild.length() > 0 ) {
artifactoryVersion = "$v.$jenkinsBuild"
}
All you need is some regular Java/Groovy code:
def jenkinsBuild = System.getenv("BUILD_NUMBER") ?: "0"
The code above uses Groovy's "elvis" operator, and is a shorthand for the following code, which uses Java's ternary operator:
def buildNumber = System.getenv("BUILD_NUMBER")
def jenkinsBuild = buildNumber != null ? buildNumber : "0"
Here's the answer to using a Java plain object (JDK8):
public class Sample {
private String region;
private String fruit;
public Sample() {
region = System.getenv().getOrDefault("REGION", null);
fruit = System.getenv().getOrDefault("FRUIT", "apple");
}
}
With the Env-Inject plugin you can get and set build parameters.
For example, under "Inject environment variables to the build process", add a Groovy script such as:
def paramsMap = [:]
def build = Thread.currentThread().executable
def my_var = build.getEnvVars()["MY_PARAM"]
if (!my_var) paramsMap.put("MY_PARAM", "default value")
// Return parameters map
out.println("Injecting parameters:\n" + paramsMap)
return paramsMap