Monday, December 24, 2012

Prepare to learn Groovy

Plan to learn Groovy, as there are 2 great frameworks written in groovy, Gradle and Grails. Besides if Grinder will support groovy, it will be a great plus.

Gradle
Gradle is a next-generation build tool, in my opinion, it will replace Maven and Ant.
Below is my build.gradle for one of my projects:

  1 apply plugin: 'war'
  2 // 'war' plugin will apply 'java' plugin automatically
  3 apply plugin: 'java'
  4 apply plugin: 'eclipse'
  5 // run web application
  6 apply plugin: 'jetty'
  7 
  8 /**
  9  * Gradle includes 2 phases: configuration and execution, refer to
 10  * http://stackoverflow.com/questions/11558824/gradle-tar-task-not-executed.
 11  *
 12  * - CONFIGURATION PHASE
 13  * In configuration phase, all codes except doFirst() and doLast() will be executed from top to bottom of the script.
 14  * The 'dependsOn' doesn't make any sense in configration phase, for example, the 'jar' and 'svnrev' tasks, if we put
 15  * 'svnrev' after 'jar', then variable 'svnrev.lastRev' can't be parsed at 'jar' task, as it completely hasn't been
 16  * initilized at all.
 17  *
 18  * - EXECUTION PHASE
 19  * In execution phase, the dependency mechanism of task will work. Be reminded that only doFirst() and doLast() will be
 20  * executed at execution phase, and gradle will finished configuration of whose script first to initialize and determine
 21  * what tasks should be executed and what is the execution order.
 22  */
 23 
 24 logger.quiet(">> Start building of $_name.$version.")
 25 /**
 26  * SHIT, here you can't give a statement like 'compileJava.options.encoding = $_sourcecode_encoding', if do so,
 27  * "Could not find property '$_sourcecode_encoding'" will be thrown out. '$_encoding' can't only be used in String?
 28  */
 29 compileJava.options.encoding = _sourcecode_encoding
 30 compileTestJava.options.encoding = _sourcecode_encoding
 31 // Define a temporary variable.
 32 //_tmp="define a temporary variable"
 33 //logger.quiet(">> Define a temporary variable: _tmp: $_tmp")
 34 
 35 // Properties added by the java plugin
 36 sourceCompatibility="1.6"
 37 targetCompatibility="1.6"
 38 //Properties added by the war plugin
 39 webAppDirName="src/main/WWW"
 40 
 41 configurations {
 42     provided {
 43         description = 'Non-exported comiple-time dependencies.'
 44     }
 45 }
 46 
 47 /**
 48  * In Gradle dependencies are grouped into configurations, and there are 4 pre-defined configuration:
 49  * compile, runtime, testCompile and testRuntime. In general dependencies of the later one will contain previous one.
 50  */
 51 dependencies {
 52     // configurationName dependencyNotation1, dependencyNotation2, ...
 53     // compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
 54 
 55     provided files('lib/DEV/j2ee/servlet-api.jar')
 56     compile fileTree(dir: 'lib', include: '**/*.jar', exclude: 'DEV/**/*.jar')
 57     /**
 58      * Below dependency will result in a exception:
 59      *      Circular dependency between tasks. Cycle includes [task ':compileJava', task ':classes'].
 60      * As sourceSets.main.output is generated by task 'compileJava', however if we declare the dependency here, it means task
 61      * 'compileJava' will depend on this file too, then a circular dependency occurs.
 62      */
 63     //compile sourceSets.main.output
 64     testCompile fileTree(dir:"lib", include:"DEV/**/*.jar")
 65 }
 66 
 67 sourceSets {
 68     /**
 69      * The Java plugin defines two standard source sets, called main and test.
 70      * Changing the project layout, the default project layout is as below:
 71      *  - src/main/java   Production Java source
 72      *  - src/main/resources  Production resources
 73      *  - src/test/java   Test Java source
 74      *  - src/test/resources  Test resources
 75      *  - src/sourceSet/java  Java source for the given source set
 76      *  - src/sourceSet/resources Resources for the given source set
 77      * Refer to http://gradle.org/docs/current/userguide/java_plugin.html and
 78      * http://gradle.org/docs/current/dsl/org.gradle.api.tasks.SourceSet.html for more information.
 79      */
 80     main {
 81         compileClasspath = compileClasspath + configurations.provided
 82         //compileClasspath.collect().each({println it})
 83         resources {
 84             srcDir 'src/main/resource'
 85         }
 86     }
 87     test {
 88         java {
 89             srcDir 'src/test/unittest'
 90             // integration test needs database, and need to import test data first.
 91             srcDir 'src/test/integration'
 92         }
 93         resources {
 94             srcDir 'src/test/resource'
 95         }
 96     }
 97 }
 98 
 99 // Retrieve the last revision of project, refer to http://stackoverflow.com/questions/9044354/checkout-svn-using-gradle.
100 task svnrev {
101     // use ant to retrieve revision.
102     ant.taskdef(resource: 'org/tigris/subversion/svnant/svnantlib.xml') {
103         classpath {
104             fileset(dir: 'lib/DEV/svnant-1.2.1', includes: '*.jar')
105         }
106     }
107     ant.svn(javahl: 'false', svnkit: 'true', username: "${_svn_user}", password: "${_svn_password}", failonerror: 'false') {
108         ant.info(target: "${_svn_source_url}", propPrefix: 'svninfo')
109     }
110     // retrieve property of ant project and assign it to a task's property, refer to:
111     // http://gradle.1045684.n5.nabble.com/can-t-find-or-extract-properties-from-svnant-info-function-in-gradle-td3335388.html
112     ext.lastRev = ant.getProject().properties['svninfo.lastRev']
113     // retrieve property of gradle project
114     //getProject().properties['buildFile']
115 }
116 
117 import org.gradle.api.java.archives.internal.DefaultManifest
118 import org.gradle.api.internal.file.IdentityFileResolver
119 task generateManifest {
120     // define a task's property
121     ext.m = new DefaultManifest(new IdentityFileResolver())
122 
123     // add some attributes
124     m.attributes([
125         'Implementation-Title':"$_name",
126         'Implementation-Version':"${version}_${svnrev.lastRev}",
127         'Implementation-Vendor':"$_company",
128         'Created-By' : _team,
129         'Build-Time' : new Date()
130     ])
131     //manifest.writeTo('build/mymanifest.mf')
132 }
133 war.dependsOn 'generateManifest'
134 
135 war {
136     archiveName = _name + ".war"
137     manifest = generateManifest.m
138 }
139 
140 // Define a global variable
141 def user_tag
142 
143 task svntag <<{
144     def console = System.console()
145     if (console) {
146         user_tag = console.readLine("> Please enter your tag(${version}): ")
147         if (!user_tag) {
148             logger.error "Please give a tag definition."
149             System.exit(0)
150         }
151     } else {
152         logger.error "Cannot get console."
153     }
154     /**
155      * We must define below logic in doFirst/Last(), otherwise it will try to make a tag each time as it is in configuration phase.
156      */
157     ant.svn(javahl: 'false', svnkit: 'true', username: "${_svn_user}", password: "${_svn_password}", failonerror: 'false') {
158         ant.copy(srcurl: "${_svn_source_url}", desturl:"${_svn_tag_url}/${user_tag}", message:"Create tag: ${_svn_tag_url}/${user_tag}")
159     }
160 }
161 
162 task dist(type: Zip) {
163     description = "Build a distribution package containing war and shell script."
164 
165     archiveName = _name + "_v${version}.zip"
166 
167     // if use include, gradle will inform 'Skipping task ':zip' as it has no source files'...why?
168     // include 'config'
169 
170     from('.') {
171         include 'README.txt'
172         include 'CHANGELOG.txt'
173     }
174     from war.destinationDir
175     into('bin') {
176         from('bin'){
177             include 'app_backup.sh'
178             include 'gen-keypair.sh'
179             include 'gen-serialkey.sh'
180         }
181     }
182     from 'etc/manual'
183 
184     doLast {
185         //print 'source of zip:' + project['zip'].source.each({println it})
186     }
187 }
188 
189 

Grails
Grails is competing with Play!, however after trying a period time of scala, it makes me dismayed and indeed its learning curve is really hard. At present, I haven't even give Groovy a simple try, my only experience is gradle build script, what makes me dizzy is its closure. Maybe after put more effort to learn Groovy, those closures will be more friendly to me.

Grinder
A great distributed performance testing tool, at present it only support jython and closure. Python is my 2nd language, in fact just knew more than scala than groovy. I have found that someone also looking for the groovy supports of grinder, and has put real effort to implement it.