Overview

Groovestry is designed to integrate the Groovy scripting language and the Tapestry web application framework. The primary features of this integration are listed below:

  • Support Groovy Scripting Language
  • Supports use in Component and Page Tapestry Specifications
  • Groovy scripts are located relative to the Tapestry Specification that reference them
  • Compiles Groovy scripts as they are referenced
  • Detect changes in Groovy scripts and recompile as needed
  • Minimal non-application script writing
  • Fully integrated with Line Precise Error Reporting
  • Groovy scripts stored in places other than file systems
  • Bean delegate usage pattern
  • Page delegate usage pattern
  • Page delegate beans integrate can be Tapestry Event Listeners
  • Support Groovy scripts deployed in production as .class files
  • Unqualified property access

Base Classes

The whole scripting framework depends upon the Page or Component in question being an extension of ScriptPage, ScriptBaseComponent, or ScriptAbstractComponent. This means that the class specified in a Tapestry specification must meet this criteria.

Script Location and Usage

A script that has the same name as the specification is automatically found and bound as the target of 'script' property. These scripts are also bound into the Tapestry Event listener for render, validate, detach, initialize, and finishLoad.

To use scripts with names other than the specification name, the ScriptBean is used with a Tapestry bean specification. The ScriptBean has a property called script that indicates the name of the script to load. The name can not include the .groovy extension.

All script files are located relative to the specification that references the script.

All scripts publish there methods that conform to proper signatures as Tapestry listeners. The names of the method is the listener name. The listeners are accessed using script."listener" or beans."beanname"."listener" where "beanname" is replaced with the name specified in the Tapestry bean specification, and listener is replaced with the method name.

Listeners are found using the following criteria.

  • The method can be static or not.
  • The return type of the method does not matter because it is ignore.
  • The method must be public.
  • For 3 parameters:
    • First must be an Object or IPage
    • Second must be an Object or IComponent
    • Third must be an Object or IRequestCycle
  • For 2 parameters:
    • First must be an Object or IPage
    • Second must be an Object or IRequestCycle
  • For 1 parameter:
    • Must be an Object or IRequestCycle

All scripts must have proper class names and packages. The class names must match the file name. When a script only contains static methods created using the def keyword, and the file does not have a class definition, Groovy automatically assigns the class name to be the file name. The package name must be the Tapestry name space, followed by script. For pages and components located in the application, the name space is application. Thus, all script files should start with 'package application.script'. If these naming rules are not followed, the script will not load.

Unqualified Property Access

If unqualified page property access is needed, the script must extend the ScriptObject class. This class provides implementations of the GroovyObject.getProperty() and GroovyObject.setProperty() methods that direct the property access to the component that owns the bean.

Scripts Deployed as .class files

To activate this feature, put a package qualifier at the top of each script file that correlates to the Tapestry namespace. For application pages and components, this looks like 'package application.script' for package components, this looks like 'package contrib.script'. All script files regardless of directory structure within the WEB-INF folder are packaged looked for in the package name specified above.

Minimal Script Writing Overhead

The minimal Groovy script is a set of function definitions that conform to a 1, 2, or 3 parameter signature.

package application.script
/*
 * This listener is called with the request cycle.
 */
def listener1(cycle) {
}
/*
 * This listener is called with the page and the request cycle.
 */
def listener2(page, cycle) {

}
/*
 * This listener is called with the page, component, and 
 * the request cycle.
 */
def listener3(page, component, cycle) {

}		
		

Tapestry Events

To link into the Tapestry page initialize, component finish load, render, validation, and detach events, the script must be the component or page script and implement appropriately named methods. The method names are derive from the interfaces in question. The whole interface does not need to be implemented, only the methods of interest need to be implemented.

Render Events, a.k.a PageRenderListener interface

This interface declares the pageBeginRender(event) and pageEndRender(event) methods

Validation Events, a.k.a. PageValidateListener interface

This interface declares the pageValidate(event) method.

Detach Events, a.k.a. PageDetachListener

This interface declares the pageDetached(event) method.

Page Initialize, a.k.a. BasePage.initialize() method

Component finish load, a.k.a. AbstractComponent.finishLoad() method

The following example shows all the methods as simply static methods that are called at appropriate points. This example also shows that the initialize() and finishLoad() methods have optional parameters.

package application.script
		
def initialize(page) {
}
def finishLoad(component) {
}
def pageBeginRender(event) {
}
def pageEndRender(event) {
}
def pageValidate(event) {
}
def pageDetached(event) {
}		

The following example shows all the methods in a class that extends ScriptObject. This allows access to the page properties from within the event handlers.

package application.script
		
import org.apache.tapestry.contrib.script.ScriptObject		
		
class TestEvent extends ScriptObject
	void initialize() {
	}
	void finishLoad() {
	}
	void pageBeginRender(event) {
	}
	void pageEndRender(event) {
	}
	void pageValidate(event) {
	}
	void pageDetached(event) {
	}		
}