How to have handler execute shell script after getting post body?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

How to have handler execute shell script after getting post body?

eaglet
I am having difficulty with getting a handler to execute a groovy shell script that was contained within a post body request.

request.body.then {
    test = it.text
    // Write test to a file
    def testFilePathName = "src/test/groovy/test.groovy"
    File testFile = new File(testFilePathName)
    testFile.write(test)

    // Execute test (running on windows)
    println "Executing test ${testFilePathName} ..."
    def testResult = ["cmd", "/c", "groovy test.groovy"].execute(null, new File("C:\\Users\\<path to folder containing test>")
    
    response.send(testResult)
}

The groovy shell script works from the groovy console.  It is just a test spec:

import spock.lang.*

class testFailureSpec extends Specification {
    def "Compare two different lists and fail"() {
        given: "Two lists"
        def actual = [1, 2, 3]
        def expected = [1, 2, 4]

        expect: "The lists should not match"
        actual == expected
    }
}
‚Äč

Two questions:
1. How to best execute an external program from inside a promise.then?
2. Is there a better way to execute a specification that is being returned within a POST body (e.g. using JunitRunner)?
Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

danhyun
Hello,

Seeing as how writing to disk and executing a process are done in a blocking manner as demonstrated, you should make use of the Blocking facility provided by Ratpack.

https://ratpack.io/manual/current/api/ratpack/exec/Blocking.html#get-ratpack.func.Factory-

In general, you can make use of Blocking.get { } to produce a Promise that executes on a Blocking thread.
There is also Promise.blockingMap that is a shortcut for executing on a Blocking threrad:
https://ratpack.io/manual/current/api/ratpack/exec/Promise.html#blockingMap-ratpack.func.Function-

Your code would look something like this:

request.body.map { it.text }.blockingMap { test ->
  def testFilePathName = "src/test/groovy/test.groovy"
    File testFile = new File(testFilePathName)
    testFile.write(test)
 return testFile
}.blockingMap { testFile ->
  ["cmd", "/c", "groovy test.groovy"].execute(null, testFile)
}.then { result ->
  response.send(result)
}


As far as testing responses there are a few options in Ratpack:

You can make use of GroovyRequestFixture https://ratpack.io/manual/current/api/ratpack/groovy/test/handling/GroovyRequestFixture.html to test handlers in isolation and consult the HandlingResult for any assertions you want to make.

You can stand up your Ratpack application via https://ratpack.io/manual/current/api/ratpack/groovy/test/GroovyRatpackMainApplicationUnderTest.html and use a testhttpclient to make a request against this and check the response generated.
Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

eaglet
Hi Dan,

Thank you for the quick response.  However, I implemented your suggestion and received the following error:

[ratpack-compute-1-2] WARN ratpack.server.internal.NettyHandlerAdapter - No response sent for POST request to /test 
(last handler: closure at line 527 of file:///C:/<path to>/ratpack.groovy)

The same error shows up even when the execution is simplified to just print out the environment variables:

["cmd", "/C", "set"].execute(null, new File("C:\\"))

A few questions:
1. Is the result variable automatically set from the output of  ["cmd", "/c", "groovy test.groovy"].execute(null, testFile) ?
2. If the script execution has an error, will the error be included in the result as well?

This would be so much easier if I could set a breakpoint at the script execution.  However, IntelliJ IDEA breakpoints aren't working.  
3. If you have any suggestions for setting breakpoints or debugging code within a Ratpack groovy script, it would really be useful.

Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

danhyun
Would you mind posting the relevant code?

IntelliJ should have no problems hitting debug break points, which version are you using? It may help to put individual statements of a closure on their own lines
Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

eaglet
This post was updated on .

given

IntelliJ IDEA version = 15.0.4
Breakpoint is set on ["cmd", "/C", "set"].execute(null, new File("C:\\"))
Ratpack is started in debug mode.

when

POST is requested with body containing script

then

+ test file does get written with contents from the POST body.
- The Breakpoint is not triggered.
- The following is displayed in the IDEA console:
[ratpack-compute-1-2] WARN ratpack.server.internal.NettyHandlerAdapter - No response sent for POST request to /test (last handler: closure at line 528 of file:///C:/<path to>/main/ratpack.groovy)

Here is the relevant code:

Closure<String> callPostTest = { context ->
    request = context.request
    response = context.response

    request.body.map { it.text }.blockingMap { test ->
        def testFilePathName = "src/test/groovy/jarvisTest.groovy"
        File testFile = new File(testFilePathName)
        testFile.write(test)   //test file is written.
        return testFile
    }.blockingMap { testFile ->
        ["cmd", "/C", "set"].execute(null, new File("C:\\"))    // Breakpoint placed here
    }.then { result ->
        response.send(result)
    }
}

ratpack {
    handlers {
        post("test") {
            callPostTest( context )
        }
    }
}

Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

eaglet
I tried moving the closure code into the post handler, but it returned the same WARN.  The breakpoint also didn't work.

ratpack {
    handlers {
        post("test") {
            request.body.map { it.text }.blockingMap { test ->
                def testFilePathName = "src/test/groovy/jarvisTest.groovy"
                File testFile = new File(testFilePathName)
                testFile.write(test)
                return testFile
            }.blockingMap { testFile ->
                ["cmd", "/C", "set"].execute(null, new File("C:\\"))    // Breakpoint placed here.
            }.then { result ->
                response.send(result)
            }
        }
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

danhyun
Hi again,

I've crafted together a small sample that works for me:

@Grab('io.ratpack:ratpack-groovy:1.2.0')
import static ratpack.groovy.Groovy.ratpack

ratpack {
  handlers {
    post("cat") {
      request.body.map { it.text }.blockingMap { name ->
        def proc = ['cat', name].execute(null, new File('C:/'))
        proc.waitFor()
        proc.text
      }.then { result ->
        response.send(result)
      }
    }
  }
}


This file is saved as C:/Users/daniel.hyun/hello.groovy

I have another file saved as C:/hello.groovy that looks like this:

def proc = ['cat', args[0]].execute(null, new File('C:/'))
println proc.text

When invoked as groovy hello.groovy

I can do the following:

[email protected] MINGW64 ~
$ curl -d "hello.groovy" localhost:5050/cat
def proc = ['cat', args[0]].execute(null, new File('C:/'))
println proc.text

[email protected] MINGW64 ~
$ curl -d "Users/daniel.hyun/hello.groovy" localhost:5050/ca t
@Grab('io.ratpack:ratpack-groovy:1.2.0')
import static ratpack.groovy.Groovy.ratpack

ratpack {
  handlers {
    post("cat") {
      request.body.map { it.text }.blockingMap { name ->
        def proc = ['cat', name].execute(null, new File('C:/'))
        proc.waitFor()
        proc.text
      }.then { result ->
        response.send(result)
      }
    }
  }
}



Let me know how this works out for you.
Reply | Threaded
Open this post in threaded view
|

Re: How to have handler execute shell script after getting post body?

eaglet
Hi Dan,  

It works!  That is, armed with your example, I was able to:

1. retrieve a test specification from the POST body text
2. save it to a file
3. execute the file in a shell (needed the PATH set properly and spock jar in ~/.groovy/lib/)
4. return the result back to the client

Thank you!
Ralph