Tag HOWTO

Tag HOWTO

This document describes how to extend the vocabulary of tags available in Weaver Application Definition (WAD) files.

Intended Audience

This document is aimed at developers who wish to extend the set of XML tags available to Weaver Application Definition (WAD) files.

Purpose

The following are among the reasons you may wish to define new XML tags.

  1. You need direct access to the objects defined by the servlet container in a manner that is not provided through Weaver's facade classes.
  2. You are extending the functionality of the core platform - rather than simply writing an application using Weaver.

Prerequisites

You should have a good understanding of Weaver and writing applications using the Weaver framework before you begin to develop new tags.

Steps

There are three steps involved in adding a new tag:

  1. Create a class (the component)that implements WeaverApplicationComponent to code the desired functionality.
  2. Create a class (the component builder) that implements WeaverDomComponentBuilder to create instances of your component from a DOM tree representation of the WAD file.
  3. Edit tagmap.xml to link the name of your new tag to your component builder.

Implementing Your New Component

The first step in adding an new tag is to implement your component according to the WeaverApplicationComponent interface.

The WeaverApplicationComponent interface looks like this:

package com.oldlight.weaver.interpreter; public interface WeaverApplicationComponent { public void attemptToHandle(WeaverExecutionContextImpl context, WeaverPrivateExecutionContext privateExecutionContext, WeaverInterpreter interpreter, WeaverInterpreterResult result) throws WeaverExecutionException; }

Its parameters are as follows:

  • context - Provides access to the Weaver classes that are facades on to the standard servlet container objects.
  • privateExecutionObject - Provides direct access to the standard servlet container objects: ServletContext, ServletConfig, HttpSession, HttpServletRequest and HttpServletResponse.
  • interpreter - Provides access to the WeaverInterpreter. Chiefly this is used to implement the execution of subtags
  • result - Tells the interpreter how to proceed after this tag has been executed.

Your new tag class should throw a WeaverExecutionException if there is a problem during its operation.

A Simple Example

The following code snippet serves to illustrates much of the plumbing needed to implement a simple tag. It shows the bulk of a simplified version of the <out>tag

public class WeaverApplicationComponentOut implements WeaverApplicationComponent { private WeaverELParameter value; private WeaverFileLocation location; public WeaverApplicationComponentOut(String value, WeaverFunctionMapper mapper, WeaverFileLocation location) throws WeaverException { this.value=new WeaverELParameter(value, mapper); this.location=location; } public void attemptToHandle(WeaverExecutionContextImpl context, WeaverPrivateExecutionContext privateContext, WeaverInterpreter interpreter, WeaverInterpreterResult interpreterResult) throws WeaverExecutionException { try { Object evaluated=value.evaluate(context); ServletOutputStream os=privateContext. getServletResponse().getOutputStream(); os.print(evaluated.toString()); interpreterResult.considerActioned=true; } catch (Exception e) { throw new WeaverExecutionException(location, "\"out\",problem outputting "+ value, e); } } }

The Constructor

The parameters to the constructor are provided by the component builder you will write for your new tag. These parameters can be whatever you need to implement the tag. Typically they are a combination of attribute values taken from the WAD file and references to various of Weaver's internal classes.

In our example:

  • value - A value of an attribute needed for our new tag, an EL expression that will be evaluated by the tag to produce a value to return to the user's browser.
  • mapper - The function mapper is needed to evaluate EL expressions.
  • location - The location in, and the name of, the WAD file from which this tag was parsed. Typically this is used for logging and for error reporting (via a WeaverExecutionException).

The constructor creates a WeaverELParameter from the value parameter. This is stored in a member variable for use during tag execution. The WeaverELParameter class provides a convenient method to evaluate expressions according to the EL language. The constructor also stores the supplied WAD file location.

The attemptToHandleMethod

The attemptToHandle method is called by Weaver to execute your tag. Our example first evaluates the value originally supplied to the constructor according to the EL with reference to the current context (i.e. the parameters in the current request etc.) The ServletOutputStream is then obtained from the WeaverPrivateExecutionContext and used to render to result of the expression evaluation.

Any exception thrown is caught and rethrown as a WeaverExecutionException. Note that the location value originally stored by our constructor is supplied to the WeaverExecutionException.

The WeaverInterpreterResult parameter is used to control the action of the interpreter after your tag has completed, it has two Boolean fields:

  • considerActioned - Set to true (the default) if the execution of your tag is to imply that the initial request has been processed.
  • continueProcessing - Set to true (the default) if the interpreter should continue execution after your tag has been processed.

A Tag with Sub-Tags

Many tags have subtags, for example the <if> tag contains the tags to be executed should the test condition evaluate to true.

In order for subtags to be correctly executed your tag must make a call back in to the interpreter.

The following code snippet is taken from the attemptToHandle method of the WeaverApplicationComponentGroup component.

... private ArrayList children=new ArrayList(); public void addComponent(WeaverApplicationComponent theComponent, WeaverFileLocation location) { children.add(new Child(theComponent, location)); } public void attemptToHandle(WeaverExecutionContextImpl context, WeaverPrivateExecutionContext privateExecutionContext, WeaverInterpreter interpreter, WeaverInterpreterResult interpreterResult) ... if (interpreter.isGuardPassed(context, guard)) { Iterator i= children.iterator(); while (i.hasNext() && interpreterResult.continueProcessing) { Child currentChild=(Child)i.next(); currentChild.childComponent. attemptToHandle(context, privateExecutionContext, interpreter, interpreterResult); } } ...

The addComponent method is called by the tag component builder repeatedly to add child components to the group. These are stored in the children ArrayList

When attemptToHandle is called it loops through all of the children calling attemptToHandle on each. That's all there is to it!

Creating a Component Builder for Your New Tag

Your component builder is responsible for creating instances of your component from a node in a DOM tree representation of the WAD file.

Your component builder must have a zero argument constructor and must implement the com.oldlight.weaver.apploader.WeaverDomComponentBuilder interface, shown below:

public interface WeaverDomComponentBuilder { WeaverApplicationComponent buildComponent(Node applicationNode, WeaverDomApplicationBuilder applicationBuilder, WeaverWADSection section, WeaverApplication application) throws WeaverParseException; }

The Weaver infrastructure will call the buildComponent method whenever an instance of your component needs to be created to represent a tag in the WAD file.

Its parameters are as follow:

  • applicationNode - is the node which represents your tag in the DOM tree representation of the WAD file.
  • applicationBuilder - is the Weaver class that controls the processing of the DOM tree representation of the WAD file. It is supplied primarily to allow the parsing of sub-tags.
  • section - defines the section of the WAD file being processed, the possible values are defined in WeaverWADSection e.g. WeaverWADSection.APPLICATION_INIT_SECTION indicates that the application/init section of the WAD file is being parsed.
  • application is the processed internal representation of the the WAD file that will be interpreted by Weaver as the application executes.
A Simple Example

Now we will consider the component builder for the simplified out component described above:

public class WeaverOutLoader implements WeaverDomComponentBuilder { public static final String ATTRIBUTE_VALUE="value"; public WeaverApplicationComponent buildComponent(Node node, WeaverDomApplicationBuilder appBuilder, WeaverWADSection section, WeaverApplication application) throws WeaverParseException { WeaverFileLocation location=appBuilder.getLocation(node); String locationMessage=WeaverDCBUtil.getLocationMessage(location); String value=WeaverDCBUtil.getAttribute(node, ATTRIBUTE_VALUE); WeaverApplicationComponentOut result=null; try { result=new WeaverApplicationComponentOut(value, application.getFunctionMapper(), location); } catch (WeaverException e) { throw new WeaverParseException(location, "Parsing \"out\", "+ "problem creating set component with "+ "\""+ATTRIBUTE_VALUE+"\"=\""+value, e); } return result; } }

The buildComponent method first looks up the value attribute in the DOM Node. This is the value to output by the tag - of course your tags may take any attributes you choose.

An instance of the appropriate component (in this case a WeaverApplicationComponentOut), is created and returned from the function.

If an error occurs an instance of WeaverParseException is thrown.

A Tag with Sub-Tags

A tag may have subtags e.g. the <if> tag contains others to be executed if its test attribute evaluates to true. A tag with sub-tags requires a little more boiler plate. The following is taken from the component builder used for the <if> tag:

... NodeList children=node.getChildNodes(); int numChildren=children.getLength(); for (int i=0; i<numChildren; i++) { Node child=children.item(i); if (child.getNodeType()==Node.ELEMENT_NODE) { String childName=child.getNodeName(); WeaverDomComponentBuilder builder=appBuilder.getBuilder(childName); if (builder!=null) { currentComponentGroup. addComponent(builder.buildComponent(child, appBuilder, section, application), appBuilder.getLocation(child)); } else { throw new WeaverParseException(appBuilder.getLocation(child), "Parsing \"if\", can't get component builder for "+ childName); } } ...

We can see that the code loops over the children of the given node in the DOM tree representation of the WAD file. For each element child it attempts to find a component builder appropriate for the child node. If the builder is found then it is used to construct the required component. The newly created child component is added to the if component via its addComponent method - it needs to keep track of its children so they can be called at execution time.

Making the Framework Aware of Your New Tag

The final stage in implementing your new tag is to tie your component builder in to Weaver. This is accomplished by editing the WEB-INF/application/conf/tagmap.xml file. The tagmap file provides a mapping from the name of a tag as it will appear in the WAD file to the corresponding component builder class.

A section of the standard tagmap.xml file is shown below:

<component-builder-registry> <component-builder name="call" class="com.oldlight.weaver.apploader.componentloaders.WeaverCallLoader"/> <component-builder name="if" class="com.oldlight.weaver.apploader.componentloaders.WeaverIfLoader"/> </component-builder-registry>

To add your component builder simply create a new <component-builder> element, setting the name attribute to be the name of your tag as it will appear in a WAD file and the class attribute to the fully qualified name of your component builder class.

Tips

UTSL
Read the source code of the standard components and component builders. They are all found in the packages interpreter.components and apploader.componentloaders respectively.
Reuse Existing Tags
Particularly the WeaverApplicationComponentGroup class. For example see the way WeaverApplicationComponentGroup is used to implement the <if> tag and the <choose>tag.