How to execute a Groovy Script from my Grails app? - grails

Well, it seems a simple task but I didn't manage to make it run.
I have a groovy script that runs fine under Windows Vista when calling from prompt:
> cd MY_GAILS_PROJECT_DIR
> groovy cp src/groovy scripts/myscript.groovy
Now, I want to execute this script (and passing to it some input arguments) through my my Maintenance Service Class (called from a controller) as below,
class MaintenanceService {
def executeMyScript() {
"groovy cp src/groovy scripts/myscript.groovy".execute()
}
}
It does not work at all! I don't even manage to have the execute() method recognizing any command (like "cd .".execute()) throwing exception:
Error 500: java.io.IOException: Cannot run program "cd": CreateProcess error=2, The system cannot find the file specified
1- How can I execute a groovy script from my grails application?
2- What are the best practices here? For instance, should I use the QuartzPlugin and then the triggerNow method for executing a script? should I use a Gant Task? If yes, how to do it?
Thank you.

If you don't mind your script running asynchronously (in a separate process to the service method), the following should work assuming groovy is on your PATH variable:
def cmd = ['groovy.bat', 'cp', 'src/groovy scripts/myscript.groovy']
cmd.execute()
If you want to view the output of the process in the application console, you should try something like this instead
// Helper class for redirecting output of process
class StreamPrinter extends Thread {
InputStream inputStream
StreamPrinter(InputStream is) {
this.inputStream = is
}
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).withReader {reader ->
String line
while ((line = reader.readLine()) != null) {
println(line)
}
}
}
}
// Execute the script
def cmd = ['groovy', 'cp', 'src/groovy scripts/myscript.groovy']
Process executingProcess = cmd.execute()
// Read process output and print on console
def errorStreamPrinter = new StreamPrinter(executingProcess.err)
def outputStreamPrinter = new StreamPrinter(executingProcess.in)
[errorStreamPrinter, outputStreamPrinter]*.start()
Update:
In response to your comment below, try the following (which assumes you're on Windows):
1: Create the file C:\tmp\foo.groovy. The content of this file should be simply:
println 'it works!'
2: In the groovy console, run the following:
cmd = ['groovy.bat', 'C:\\tmp\\foo.groovy']
cmd.execute().text
3: You should see the result of the script (the text 'it works!') shown in the Groovy console
If you can't get this simple example working, there's something wrong with your environment, e.g. 'groovy.bat' is not on your PATH. If you can get this example working, then you should be able to work forward from it to achieve your objective.

As of grails 1.3.6 the run-script command is built in to let you run
grails run-script myScript.groovy
For earlier versions of grails, check out my updated blog post from what Carlos posted above.

Easiest Way:
Generate an Groovy Class and place at in your /src/groovy Folder of your Grails Project.
Import that Class in your Domain Class and use the Functions you defined.
My 2 Cents...

This might help as well:
http://naleid.com/blog/2008/03/31/using-gant-to-execute-a-groovy-script-within-the-grails-context-updated/
Carlos

Another decision you can use GroovyScriptEngine for example:
file MyScript.groovy:
static String showMessage() {
println("Message from showMessage")
}
file BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
new GroovyScriptEngine("scripts")
.loadScriptByName("MyScript.groovy")
.showMessage()
}
def destroy = {
}
}

Related

Executing shell commands from inside Pipeline Shared Library

I'm writing a shared library that will get used in Pipelines.
class Deployer implements Serializable {
def steps
Deployer(steps) {
this.steps = steps
}
def deploy(env) {
// convert environment from steps to list
def process = "ls -l".execute(envlist, null)
process.consumeProcessOutput(output, error)
process.waitFor()
println output
println error
}
}
In the Jenkinsfile, I import the library, call the class and execute the deploy function inside a script section:
stage('mystep') {
steps {
script {
def deployer = com.mypackage.HelmDeployer("test")
deployer.deploy()
}
}
}
However, no output or errors are printed on the Console log.
Is it possible to execute stuff inside a shared library class? If so, how, and what am I doing wrong?
Yes, it is possible but not really an obvious solution. Every call that is usually done in the Jenkinsfile but was moved to the shared-library needs to reference the steps object you passed.
You can also reference the Jenkins environment by calling steps.env.
I will give you a short example:
class Deployer implements Serializable {
def steps
Deployer(steps) {
this.steps = steps
}
def callMe() {
// Always call the steps object
steps.echo("Test")
steps.echo("${steps.env.BRANCH_NAME}")
steps.sh("ls -al")
// Your command could look something like this:
// def process = steps.sh(script: "ls -l", returnStdout: true).execute(steps.env, null)
...
}
}
You also have to import the object of the shared library and create an instance of it. Define the following outside of your Pipeline.
import com.mypackage.Deployer // path is relative to your src/ folder of the shared library
def deployer = new Deployer(this) // 'this' references to the step object of the Jenkins
Then you can call it in your pipeline as the following:
... script { deployer.test() } ...

Groovy - How to reflect code from another groovy app to find Classes/Properties/Types within

First, I came from a .NET background so please excuse my lack of groovy lingo. Back when I was in a .NET shop, we were using TypeScript with C# to build web apps. In our controllers, we would always receive/respond with DTOs (data xfer objects). This got to be quite the headache every time you create/modify a DTO you had to update the TypeScript interface (the d.ts file) that corresponded to it.
So we created a little app (a simple exe) that loaded the dll from the webapp into it, then reflected over it to find the DTOs (filtering by specific namespaces), and parse through them to find each class name within, their properties, and their properties' data types, generate that information into a string, and finally saved as into a d.ts file.
This app was then configured to run on every build of the website. That way, when you go to run/debug/build the website, it would update your d.ts files automatically - which made working with TypeScript that much easier.
Long story short, how could I achieve this with a Grails Website if I were to write a simple groovy app to generate the d.ts that I want?
-- OR --
How do I get the IDE (ex IntelliJ) to run a groovy file (that is part of the app) that does this generation post-build?
I did find this but still need a way to run on compile:
Groovy property iteration
class Foo {
def feck = "fe"
def arse = "ar"
def drink = "dr"
}
class Foo2 {
def feck = "fe2"
def arse = "ar2"
def drink = "dr2"
}
def f = new Foo()
def f2 = new Foo2()
f2.properties.each { prop, val ->
if(prop in ["metaClass","class"]) return
if(f.hasProperty(prop)) f[prop] = val
}
assert f.feck == "fe2"
assert f.arse == "ar2"
assert f.drink == "dr2"
I've been able to extract the Domain Objects and their persistent fields via the following Gant script:
In scripts/Props.groovy:
import static groovy.json.JsonOutput.*
includeTargets << grailsScript("_GrailsBootstrap")
target(props: "Lists persistent properties for each domain class") {
depends(loadApp)
def propMap = [:].withDefault { [] }
grailsApp.domainClasses.each {
it?.persistentProperties?.each { prop ->
if (prop.hasProperty('name') && prop.name) {
propMap[it.clazz.name] << ["${prop.name}": "${prop.getType()?.name}"]
}
}
}
// do any necessary file I/O here (just printing it now as an example)
println prettyPrint(toJson(propMap))
}
setDefaultTarget(props)
This can be run via the command line like so:
grails props
Which produces output like the following:
{
"com.mycompany.User": [
{ "type": "java.lang.String" },
{ "username": "java.lang.String" },
{ "password": "java.lang.String" }
],
"com.mycompany.Person": [
{ "name": "java.lang.String" },
{ "alive": "java.lang.Boolean" }
]
}
A couple of drawbacks to this approach is that we don't get any transient properties and I'm not exactly sure how to hook this into the _Events.groovy eventCompileEnd event.
Thanks Kevin! Just wanted to mention, in order to get this to run, here are a few steps I had to make sure to do in my case that I thought I would share:
-> Open up the grails BuildConfig.groovy
-> Change tomcat from build to compile like this:
plugins {
compile ":tomcat:[version]"
}
-> Drop your Props.groovy into the scripts folder on the root (noting the path to the grails-app folder for reference)
[application root]/scripts/Props.groovy
[application root]/grails-app
-> Open Terminal
gvm use grails [version]
grails compile
grails Props
Note: I was using Grails 2.3.11 for the project I was running this on.
That gets everything in your script to run successfully for me. Now to modify the println portion to generate Typescript interfaces.
Will post a github link when it is ready so be sure to check back.

Can a Job DSL script be tested

Ideally I'd like to be able to invoke the script with some kind of unit test before I have it execute on a Jenkins.
Is there any way to test a Job DSL script other than having jenkins run it?
Besides the examples in job-dsl-gradle-example, you can also go a step further and write tests for individual files or jobs. For example let's assume you have a job configuration file located in jobs/deployJob.groovy
import javaposse.jobdsl.dsl.DslScriptLoader
import javaposse.jobdsl.dsl.MemoryJobManagement
import javaposse.jobdsl.dsl.ScriptRequest
import spock.lang.Specification
class TestDeployJobs extends Specification {
def 'test basic job configuration'() {
given:
URL scriptURL = new File('jobs').toURI().toURL()
ScriptRequest scriptRequest = new ScriptRequest('deployJob.groovy', null, scriptURL)
MemoryJobManagement jobManagement = new MemoryJobManagement()
when:
DslScriptLoader.runDslEngine(scriptRequest, jobManagement)
then:
jobManagement.savedConfigs.each { String name, String xml ->
with(new XmlParser().parse(new StringReader(xml))) {
// Make sure jobs only run manually
triggers.'hudson.triggers.TimerTrigger'.spec.text().isEmpty()
// only deploy every environment once at a time
concurrentBuild.text().equals('false')
// do a workspace cleanup
buildWrappers.'hudson.plugins.ws__cleanup.PreBuildCleanup'
// make sure masked passwords are active
!buildWrappers.'com.michelin.cio.hudson.plugins.maskpasswords.MaskPasswordsBuildWrapper'.isEmpty()
}
}
}
}
This way you are able to go through every XML node you want to make sure to have all the right values set.
Have a look at the job-dsl-gradle-example. The repo contains a test for DSL scripts.
Doing it in the same way as crasp but using Jenkins test harness as explained in Jenkins Unit Test page, which is slower but would work with auto-generated DSL giving syntax errors as explained here.
After setting the code as explained here, you can just do a test like this one:
#Unroll
void 'check descriptions #file.name'(File file) {
given:
JobManagement jobManagement = new JenkinsJobManagement(System.out, [:], new File('.'))
Jenkins jenkins = jenkinsRule.jenkins
when:
GeneratedItems items = new DslScriptLoader(jobManagement).runScript(file.text)
then:
if (!items.jobs.isEmpty()) {
items.jobs.each { GeneratedJob generatedJob ->
String text = getItemXml(generatedJob, jenkins)
with(new XmlParser().parse(new StringReader(text))) {
// Has some description
!description.text().isEmpty()
}
}
}
where:
file << TestUtil.getJobFiles()
}

grails: guidance on writing scripts, esp for calling existing database-migration scripts

My requirement is to invoke some processing from a Jenkins build server, to determine whether the domain model has changed since the last build. I've come to the conclusion that the way forward is to write a script that will invoke a sequence of existing scripts from the db-migration plugin. Then I can invoke it in the step that calls test-app and war.
I've looked in the Grails doc, and at some of the db-migration scripts, and I find I'm stuck - have no idea where to start trying things. I'd be really grateful if someone could point me at any suitable sources. BTW, I'm a bit rusty in Grails. Started to teach myself two years ago via proof of concept project, which lasted 6 months. Then it was back to Eclipse rich client work. That might be part of my problem, though I never go involved in scripts.
One thing I need in the Jenkins evt is to get hold of the current SVN revision number being used for the build. Suggestions welcome.
Regards, John
Create a new script by running grails create-script scriptname. The database-migration plugins scripts are configured to be easily reused. There are is a lot of shared code in _DatabaseMigrationCommon.groovy and each script defines one target with a unique name. So you can import either the shared script or any standalone script (or multiple scripts) and call the targets like they're methods.
By default the script generated by create-script "imports" the _GrailsInit script via includeTargets << grailsScript("_GrailsInit") and you can do the same, taking advantage of the magic variables that point at installed plugins' directories:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
If you do this you can remove the include of _GrailsInit since it's already included, but if you don't that's fine since Grails only includes files once.
Then you can define your target and call any of the plugin's targets. The targets cannot accept parameters, but you can add data to the argsMap (this is a map Grails creates from the parsed commandline arguments) to simulate user-specified args. Note that any args passed to your script will be seen by the database-migration plugin's scripts since they use the same argsMap.
Here's an example script that just does the same thing as dbm-generate-changelog but adds a before and after message:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
target(foo: "Just calls dbmGenerateChangelog") {
println 'before'
dbmGenerateChangelog()
println 'after'
}
setDefaultTarget foo
Note that I renamed the target from main to foo so it's unique, in case you want to call this from another script.
As an example of working with args, here's a modified version that specifies a default changelog name if none is provided:
println 'before'
if (!argsMap.params) {
argsMap.params = ['foo2.groovy']
}
dbmGenerateChangelog()
println 'after'
Edit: Here's a fuller example that captures the output of dbm-gorm-diff to a string:
includeTargets << new File("$databaseMigrationPluginDir/scripts/_DatabaseMigrationCommon.groovy")
target(foo: "foo") {
depends dbmInit
def configuredSchema = config.grails.plugin.databasemigration.schema
String argSchema = argsMap.schema
String effectiveSchema = argSchema ?: configuredSchema ?: defaultSchema
def realDatabase
boolean add = false // booleanArg('add')
String filename = null // argsList[0]
try {
printMessage "Starting $hyphenatedScriptName"
ByteArrayOutputStream baos = new ByteArrayOutputStream()
def baosOut = new PrintStream(baos)
ScriptUtils.executeAndWrite filename, add, dsName, { PrintStream out ->
MigrationUtils.executeInSession(dsName) {
realDatabase = MigrationUtils.getDatabase(effectiveSchema, dsName)
def gormDatabase = ScriptUtils.createGormDatabase(dataSourceSuffix, config, appCtx, realDatabase, effectiveSchema)
ScriptUtils.createAndPrintFixedDiff(gormDatabase, realDatabase, realDatabase, appCtx, diffTypes, baosOut)
}
}
String xml = new String(baos.toString('UTF-8'))
def ChangelogXml2Groovy = classLoader.loadClass('grails.plugin.databasemigration.ChangelogXml2Groovy')
String groovy = ChangelogXml2Groovy.convert(xml)
// do something with the groovy or xml here
printMessage "Finished $hyphenatedScriptName"
}
catch (e) {
ScriptUtils.printStackTrace e
exit 1
}
finally {
ScriptUtils.closeConnection realDatabase
}
}
setDefaultTarget foo

Grails: importing plugin classes to _Events.groovy

I've created a Grails plugin which adds a custom test type class (extending GrailsTestTypeSupport) and custom test result class (extending GrailsTestTypeResult) to support a custom test type that I run during the other phase of the test-app script. Testing this on my local machine has gone swimmingly but...
When I packaged the plugin to use in my app, the tests are blowing up on our CI server (Jenkins). Here's the error that Jenkins is spitting out:
unable to resolve class CustomTestResult # line 58, column 9.
new CustomTestResult(tests.size() - failed, failed)
It appears that I cannot simply import these classes into _Events.groovy, and the classes are not otherwise on the classpath. But I'll be damned if I can figure out how to get them onto the classpath. Here's what I have so far (in _Events.groovy):
import java.lang.reflect.Constructor
eventAllTestsStart = {
if (!otherTests) otherTests = []
loadCustomTestResult()
otherTests << createCustomTestType()
}
private def createCustomTestType(String name = 'js', String relativeSourcePath = 'js') {
ClassLoader parent = getClass().getClassLoader()
GroovyClassLoader loader = new GroovyClassLoader(parent)
Class customTestTypeClass = loader.parseClass(new File("${customTestPluginDir}/src/groovy/custom/test/CustomTestType.groovy"))
Constructor customTestTypeConstructor = customTestTypeClass.getConstructor(String, String)
def customTestType = customTestTypeConstructor.newInstance(name, relativeSourcePath)
customTestType
}
private def loadCustomTestResult() {
ClassLoader parent = getClass().getClassLoader()
GroovyClassLoader loader = new GroovyClassLoader(parent)
Class customTestResultClass = loader.parseClass(new File("${customTestPluginDir}/src/groovy/custom/test/CustomTestResult.groovy"))
}
Currently: CustomTestResult is only referenced from within CustomTestType. As far as I can tell, _Events.groovy is loading CustomTestType but it is failing because it then insists that CustomTestResult is not on the classpath.
Putting aside for a moment that it seems crazy that there's this much overhead to get plugin-furnished classes onto the classpath for the test cycle to begin with... I'm not quite sure where I've gotten tripped up. Any help or pointers would be greatly appreciated.
Have you tried simply loading the class in question via the ClassLoader that is accessible via the classLoader variable in _Events.groovy?
Class customTestTypeClass = classLoader.loadClass('custom.test.CustomTestType')
// use nice groovy overloading of Class.newInstance
return customTestTypeClass.newInstance(name, relativeSourcePath)
You should be late enough in the process at eventAllTestsStart for this to be valid.
#Ian Roberts' answer got me pointed in roughly the right direction, and combined with the _Events.groovy script from this grails-cucumber plugin, I managed to come through with this solution:
First, _Events.groovy became this:
eventAllTestsStart = { if (!otherTests) otherTests = [] }
eventTestPhasesStart = { phases ->
if (!phases.contains('other')) { return }
// classLoader.loadClass business per Ian Roberts:
otherTests << classLoader.loadClass('custom.test.CustomTestType').newInstance('js', 'js')
}
Which is far more readable than where I was at the start of this thread. But: I was in roughly the same position: my ClassNotFoundException moved from being thrown in _Events.groovy to being thrown from within CustomTestType when it tried to create an instance of custom.test. CustomTestResult. So within CustomTestType, I added the following method:
private GrailsTestTypeResult createResult(passed, failed) {
try {
return new customTestResult(passed, failed)
} catch(ClassNotFoundException cnf) {
Class customTestResult = buildBinding.classLoader.loadClass('custom.test.CustomTestResult')
return customTestResult.newInstance(passed, failed)
}
}
So Ian was right, inasmuch as classLoader came to the rescue -- I just wound up needing its magic in two places.

Resources