|
|
|
Writing Groovy handlers
Groovy can be used to implement handlers within Zero applications. The following sections of this article provide information about writing Groovy handlers.
Configuration
Groovy scripts and templates are generally used without explicit configuration in zero.config. For example, these files are generally served from the public folder, processed as resource handlers, and used as renderers. Such usage just requires that the file be placed in the appropriate directories.
For those advanced cases where you want to register a Groovy script or template as an event handler in zero.config (e.g. if you want to use a Groovy script to implement a start event handler), see the configuration guide for details.
Groovy scripts and classes
Groovy-based event handlers may be scripts or classes. Scripts may be a unstructured, such as:
println "Hello World"
or written as a set of event-specific methods, such as:
def onGET() {
println "Hello World"
}
Most events support a "method-then-script" invocation strategy: That is, the unstructured script is invoked only if the corresponding method is not found. The exception is the "resource events" (i.e. "list", "retrieve", etc.), which support only method-based invocation. So, Groovy-based resource handlers must be either scripts with methods or classes.
Note that the "invoke events" (i.e. "GET", "POST", etc) are treated as a group in regards to "method-then-script" invocation. Thus, if any of the group methods are defined (e.g. onGET), then the request can only be served by a method -- the unstructured script will not be invoked.
Groovy classes include the more formal decorations of an object-oriented programming model, such as:
import zero.core.groovysupport.ZeroObject
public class MyHandler implements ZeroObject {
public void onGET() {
println "Hello World"
}
}
ZeroObject is a marker interface. Implementations of ZeroObject and Groovy scripts obtain special Zero support in the form of Zero bindings and print and println bound to the Zero writer.
Bindings
Groovy supports bindings as a mechanism to set variables and methods on a script. Zero provides default bindings and a mechanism for you to add additional bindings.'
Bindings do not work within static methods and blocks because the check to see if they are defined is done at compile-time when they are not yet available.
Default bindings
Additional bindings
Defining additional bindings is a two-step process. First, you must provide a Java implementation of zero.core.groovysupport.bindings.BindingHandler to define the bindings. Examples can be found in the Zero source code within the zero.core.groovysupport.bindings package.
The second step is to register your BindingHandler with a script type (based upon file suffix, such as .groovy or .gt). For example:
/config/bindings/.groovy += ["zero.core.groovysupport.bindings.DefaultBindings"]
Global Context accessors
Global Context accessors are objects that support Groovy-friendly interactions with the Global Context. These objects support GPath syntax for read and write, as demonstrated below:
| Global Context API | Groovy equivalent |
zget("/request/method") | def m == request.method[] |
zget("/request/params/name#1") | def name == request.params.name[1] |
zput("/request/status", 204) | request.status == 204 |
In general, a Global Context URI is structured as /<zone>/<path>[#<value_path>]. Global Context accessors support GPath notation for the zone/path portion; the value path is expressed within brackets (without the '#').
Details about value pathing within the Global Context are found in the Global Context section.
Groovy scripts invoking Groovy scripts
Groovy scripts in the public and app/resources virtualized directories can access other Groovy scripts and classes from the app/scripts virtualized directory.
Invoking a Groovy class
To use a Groovy class defined in the scripts folder, import the appropriate class name (including package names). You can either instantiate the class or invoke static methods in the class. In the example below, we define a class called Helper in a
file called app/scripts/help/support/Helper.groovy.
package help.support
class Helper {
def help() {
return "Helping"
}
}
This class can be used from another script with the following snippet of code:
import help.support.Helper
h = new Helper()
h.help()
Invoking a Groovy script
The steps to define and import a Groovy script from the app/scripts virtualized directory are similar. Groovy scripts may be invoked as an unstructured script (invokeScript) or by method (invokeMethod). For example, define a script in file app/scripts/helper.groovy:
print "Helper Script"
def helperMethod() {
print "Helper Method"
}
def helperMethodWithArgs(foo, bar) {
print "Helper Method with $foo $bar"
}
This script may be invoked by the following:
// To run the unstructured script: results are "Helper Script"
invokeScript ( "helper.groovy" )
// To invoke a specific method: results are "Helper Method"
invokeMethod ( "helper.groovy", "helperMethod", null )
// To invoke a specific method with args: results are "Helper Method x y"
invokeMethod ( "helper.groovy", "helperMethod", "x", "y" )
Using Groovy templates
Zero treats files with a .gt suffix as Groovy templates. Groovy templates can be used when a file predominantly
contains static text content. Zero uses Groovy's SimpleTemplateEngine to provide template support.
Here is an example of a Groovy template :
<html>
<body>
<h1>
Today is <%= new Date() %>
</h1>
<% for (name in ['john', 'jack', 'joe']) { %>
<ul>
<li>${name}</li>
</ul>
<% } %>
</body>
</html>
Groovy statements are enclosed with a <% and %>. Groovy expressions that evaluate to strings can
be placed within <%= and %>.
Rendering templates to the output stream
The View renderer renders a template directly to the output stream. This is useful when you're invoking the template in situ for the response.
The View renderer is invoked from a Groovy template as:
<%
request.view="otherTemplate.gt"
render()
%>
Rendering templates to a String
Templates may be used to render Strings for other processing, such as content items passed into the Atom renderer. This is accomplished with the invokeTemplate method, such as:
def content = invokeTemplate("foo/bar/mytemplate.gt")
// process content
Similar to the View renderer, the path (e.g., "foo/bar/template.gt") must be relative to your application's app/views virtualized directory.
Logging from a Groovy Script
Groovy scripts and templates have a binding for logger, which is an instance of zero.core.logging.GroovyLogger. logger supports two forms of invocation.
In each invocation form, the log message is specified as a Closure. The Closure is not evaluated unless the specified log level is met.
Invoking with a Closure
This is the simpler form of the logging API. The sourceClass and sourceMethod are derived from the Closure.
File: <appRoot>/public/a/b/test.groovy
package a.b
def onGET() {
logger.FINE { "Hello world." }
}
Sample excerpt from <appRoot>/config/logging.properties
This sample shows logging specified for exactly this Groovy artifact.
a.b.test.level=ALL
Sample log results
2007-08-09 21:42:08 a.b.test::onGET Thread-10
FINE [ Hello world. ]
Invoking with named parameters
This invocation form allows the caller to optionally specify the sourceClass and sourceMethod. If not specified, sourceClass and sourceMethod are derived from the Closure.
File: <appRoot>/public/a/b/test.groovy
The following example shows a possible usage that enables arbitrary grouping of Groovy artifacts, which might be handy for specifying consolidated log levels.
package a.b
def onGET() {
logger.FINE ( clazz: "groovy.a.b.test", method: "boo", msg: { "Hello world." })
}
Sample excerpt from <appRoot>/config/logging.properties
groovy.level=ALL
Sample log results
9:42:23 PM groovy.a.b.test::boo Thread-11
FINE [ Hello world. ]
Refer to the troubleshooting section for more information about logging and tracing.
Troubleshooting
Why is my Groovy script failing with "...InvokerInvocationException: No such property: ..."?
Missing classes might be reported as "No such property", due to Groovy's attempt to interpret the package as a GPath expression. For example, the following misspelled package:
zero.kore.utils.URIUtils.getRelativeUri("")
results in
"...InvokerInvocationException: No such property: zero"
|