Writing Groovy handlers

Groovy can be used to implement handlers within IBM® WebSphere® sMash applications.

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 requires that the file be placed in the appropriate directories. For more information, see the following topics:

file serving
See the File serving article.
resource handlers
See the Resource (REST) handling article.
renderers
See the Response rendering article.

For those advanced cases where you want to register a Groovy script or template as an event handler in zero.config (for example, if you want to use a Groovy script to implement a requestBegin event handler), see the Event processing and event handler registration section of the Configuration articles for details.

Groovy scripts and classes

Groovy-based event handlers might be scripts or classes. Scripts can be a unstructured, as shown in the following example:

println "Hello World"

Scripts can also be written as a set of event-specific methods, as shown in the following example:

def onGET() {
    println "Hello World"
}

Most events support a "method-then-script" invocation strategy. This means that the unstructured script is invoked only if the corresponding method is not found. The exception is resource events (for example list and retrieve) that support only method-based invocation. Groovy-based resource handlers must be either scripts with methods or classes.

Invoke events (for example, GET and POST) are treated as a group in a method-then-script invocation. Therefore, if any of the group methods are defined (for example onGET), then the request can only be served by a method and the unstructured script is not invoked.

Groovy classes include the more formal decorations of an object-oriented programming model, as shown in the following example:

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 WebSphere sMash support in the form of bindings and print and println bound to the WebSphere sMash writer. For more information on bindings, see the Bindings section.

Bindings

Groovy supports bindings as a mechanism to set variables and methods on a script. WebSphere sMash provides default bindings and a mechanism for you to add additional bindings.

Bindings do not work within static methods and blocks because a check (to see if they are defined) is done at compile-time when they are not yet available.

Default bindings

The following table shows default bindings.

Name Description
app Global context accessor bound to /app.
config Global context accessor bound to /config .
event Global context accessor bound to /event .
headers Global context accessor bound to /request/headers .
request Global context accessor bound to /request .
user Global context accessor bound to /user .
zget() Set of convenience methods from GlobalContext
zput()
zputs()
zpost()
zdelete()
zlist()
zlistAll()
zcontains()
zdump()
logger Instance of zero.core.logging.GroovyLogger for logging from a Groovy script
render() Invokes the zero.core.views.ViewEngine.render() renderer
getRelativeUri() Set of convenience methods from Class URIUtils
getAbsoluteUri()
getRequestedUri()
getVFile() Returns a File reference based on the virtualized directories. The method is bound to zero.core.utils.VDirectory.getVFile(String, List)
listFiles() Returns a list of File found within a virtualized directory. The method is bound to zero.core.utils.VDirectory.listFiles(String)

Additional bindings

Defining additional bindings is a two-step process, as described in the following steps:

  1. Provide a Java implementation of zero.core.groovysupport.bindings.BindingHandler to define the bindings. Examples can be found in the WebSphere sMash source code within the zero.core.groovysupport.bindings package.
  2. Register your BindingHandler with a script type (based on the file suffix, such as .groovy or .gt as shown in the following example:
    /config/bindings/.groovy += ["zero.core.groovysupport.bindings.DefaultBindings"]

Global context accessors

Global context accessors are objects that support Groovy interactions with the global context. These objects support GPath syntax for read and write, as shown in the following table:

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

A global context URI is generally 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 using value paths within the global context are found in the Global context article.

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 following example, a Helper class is defined in a app/scripts/help/support/Helper.groovy file.

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 can be invoked as an unstructured script (invokeScript) or by method (invokeMethod). The following example defines a script in the app/scripts/helper.groovy file:

print "Helper Script"

def helperMethod() {
   print "Helper Method"
}

def helperMethodWithArgs(foo, bar) {
   print "Helper Method with $foo $bar"
}

This script can be invoked by the running the following example:

// 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

WebSphere sMash treats files with a .gt suffix as Groovy templates. Groovy templates can be used when a file contains mostly static text content. WebSphere sMash uses Groovy's SimpleTemplateEngine to provide template support. The following example shows 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 open and closed angle brackets: <% 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. See the view renderer information in the Response rendering article for more information. This is useful when you are invoking the template in situ for the response. The view renderer is invoked from a Groovy template as shown in the following example:

<%
 request.view="otherTemplate.gt"
 render()
%>

Rendering templates to a String

Templates can be used to render strings for other processing, such as content items passed into the Atom renderer. This is accomplished with the invokeTemplate method, as shown in the following example:

def content = invokeTemplate("foo/bar/mytemplate.gt")
// process content

Similar to the view renderer, the path (for example foo/bar/template.gt) must be relative to the app/views of your application 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. The logger binding 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

Invoking with a closure is a simpler form of the logging API. The sourceClass and sourceMethod are derived from the closure as shown in the following example.

File: <appRoot>/public/a/b/test.groovy

package a.b

def onGET() {
   logger.FINE { "Hello world." }
}

The following sample is an excerpt from: <appRoot>/config/logging.properties and shows logging specified for exactly this Groovy artifact.

a.b.test.level=ALL

The following sample shows log results.

2007-08-09 21:42:08 a.b.test::onGET Thread-10
   FINE [ Hello world. ]

Invoking with named parameters

Invoking with named parameters 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." })
}

The following sample is an excerpt from: <appRoot>/config/logging.properties.

groovy.level=ALL

The following sample shows log results.

9:42:23 PM groovy.a.b.test::boo Thread-11
   FINE [ Hello world. ]

Troubleshooting

Groovy Class Reloading Behavior

In most instances, the WebSphere sMash runtime will force a reload of any .groovy files that are modified on the file system. This behavior allows a very dynamic development environment, where the Groovy file can be edited with the application running, and the script will be recompiled on the next request.

There are exceptions to when this is possible, however. The WebSphere sMash runtime will not traverse across all of the used Groovy scripts on an invocation, so if a Groovy script calls another Groovy script, then the called script's compiled classes will not be updated if the script was updated.

The net result of this behavior is that if a Groovy script is always only called from other Groovy scripts, that called Groovy script will only be compiled once. For changes to that script to take effect, the application will have to be recycled.

InvokerInvocationException: No such property

If your Groovy script is failing with the ...InvokerInvocationException: No such property: ... message it is because missing classes might be reported as "No such property", due to Groovy's attempt to interpret the package as a GPath expression. For example, consider the following misspelled package:

zero.kore.utils.URIUtils.getRelativeUri("")

This example produces the following results:

"...InvokerInvocationException: No such property: zero"

Version 1.0.0.3.25591