velocity-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Brad Cox <b...@virtualschool.edu>
Subject Re: Can I write a macro that can call itself?
Date Fri, 13 Jul 2001 01:15:36 GMT
At 12:43 PM -0700 7/12/01, Uday Kumar wrote:
>I am very new to velocity. i have a question... I wanna do recursion using
>velocity. There is an example in the documentation where the .vm file calls
>itself. But, what i am wondering is - Can a macro call itself? Is that
>permitted in velocity? "Can I write a macro that can call itself?" is my
>ultimate question....

I didn't stick with velocity long enough to be absolutely sure but 
I'm pretty sure you can't. Only programmers should want to do 
recursion; info architects shouldn't, y'know ;)

But you can in JWAA: http://virtualschool.edu/jwaa, once I finishing 
testing/uploading the new release (tomorrow I hope). Here's the 
relevant dox

Using JWAA as a Template Language

First generation web app development languages such as ASP and JSP 
make no distinction between views and controllers. Each page of a web 
application is a single file with java code mixed with html, 
separated only by various <%escape sequences%>. Next-generation 
template languages separate the two functions. Software developers 
work with files that contain only java code while information 
architects work with files that only contain an extended form of html 
called a template language.

In Velocity, for example, programmers write java classes to provide 
the page's overall control flow and to marshall data to be presented 
to the user by querying a datbase (deprecated) or an intermediary 
bean. The data is marshalled into a hashtable from which the template 
language will acquire it by name. Information architects use the 
template language to present the data to the user. The template 
languages provides enough dynamic capability to loop over variable 
length data streams, to test conditions, and to expand variables into 
the outgoing html stream.

The 1.2 release of JWAA shows that the same objective can be 
accomplished in different way by using Java as the extension language 
and binding the html and code together at compile time. Since no 
additional languages are needed, the savings in speed, size, and 
security are substantial. JWAA's core classes are only 28k compared 
to Velocity's 373kb (compressed jar sizes).

JWAA Overview

In JWAA, a dynamic web page is a singleton instance of the Page 
class. Page is an application-specific class that provides page 
layout and formatting methods to its subclasses, and is in turn a 
subclass of JWAA's AbstractPage. This defines an abstract method, 
controller(Ctx ctx) to require that each concrete subclass to 
override it. The Ctx argument contains the servlet context 
information that applications need to interact with the browser, the 
application's log file, and the application's pool of database 
connections.

AddressPage is an example of such a class. The html strings were not 
written by hand. Rather an input file was preprocessed by the MLS 
string processor to generate the java code shown at this link. 
Although presentation (the html in the view method) and logic (the 
controller method) are cleanly separated into different methods, both 
are in a single class. This works fine if responsibility for each 
page is held by a single individual. But if coding (java) and 
presentation (html) responsibilities are divided, a different 
approach is needed.

This paper is to document a simple technique for separating code and 
presentation into distinct files while maintaining all the other 
advantages of the JWAA approach (small size, compile-time 
type-checking, field classes and so forth).

Using JWAA as a Template Language

The technique involves a simple extension to the MLS preprocessor 
such that <<filename>> is recognized as a command to include the 
contents of filename after processing it as an MLS string. This can 
be used to divides each web page into one or more files. The pure 
java part of the above example contains the controller method plus 
the declaration for as many view methods as the controller might 
require:


class AddressPage extends Page
{
   public final static AddressPage instance = new AddressPage();
   private AddressPage()
   {
     super( "DemoAddressPage", Role.Registered, "AddressPage", "Demo 
AddressPage");
   }
   public void controller(Ctx ctx) throws Exception
   {
     AccountBean account = (AccountBean)ctx.getViewer();
     AddressBean address = account.getAddress(ctx);

     Op op = (Op)ctx.getField("op", Op.Null);
     Street street = (Street)ctx.getField("street", address.getStreet());
     City city = (City)ctx.getField("city", address.getCity());
     USState state = (USState)ctx.getField("state", address.getState());
     Zipcode zipcode = (Zipcode)ctx.getField("zipcode", address.getZipcode());
     Phone phone = (Phone)ctx.getField("phone", address.getPhone());

     if ( op.ok() && street.ok() && city.ok() &&
       state.ok() && zipcode.ok() && phone.ok())
     {
       address.setAddress( street, city, state, zipcode, phone);
       address.save(ctx);
       AccountPage.instance.controller(ctx);
     }
     else viewAddress(ctx, op, street, city, state, zipcode, phone);
   }
   private final void viewAddress(
     Ctx ctx, Op op, Street street,
     City city, USState state,
     Zipcode zipcode, Phone phone)
   throws Exception
   {
     <<pathname/AddressPageTemplate.tm>>
   }
}


The body of each view method are now stored in separate files whose 
names are specified as <<pathname/AddressPageTemplate.tm>>.


	ctx.send({{
<html>
<body>
<h2 align=center>Address and Telephone</h2>
{{ emitForm(ctx) }}
<p><TABLE BORDER="0" CELLPADDING="5" CELLSPACING="5">
<TR valign=top>
  <TH valign=top>Address</TH>
  <TD><input name=street size=32 maxlength=32 value="{{ street }}" >
   Street {{ fontRed(street.getMessage()) }}
   <br><input name=city size=32 maxlength=32 value="{{ city }}" >
   City {{ fontRed(city.getMessage()) }}
   <br><input name=state size=2 maxlength=2 value="{{ state }}">
   State {{ fontRed(state.getMessage()) }}
   <input name=zipcode size=10 maxlength=10 value="{{ zipcode }}" >
   Zipcode {{ fontRed(zipcode.getMessage()) }}
  </TD>
</TR>
<TR valign=top>
  <TH valign=top>Phone</TH>
  <TD><input name=phone size=32 maxlength=32 value="{{ phone }}" >
   10 digit US-style numbers, area code first.  {{ 
fontRed(phone.getMessage()) }} </TD>
</TR>
<tr valign=top>
  <th><input
   type=submit
   name=op
   value="OK"
  ></th><td>{{ msg }}</td>
</tr>
</table>
</form>
</body>
</html>}});


Currently, a limintation of this technique is that the 
ctx.send({{...}}) statement must be hand-coded into each template 
file. This limitation is required so that conditional (if then else) 
and looping (for, while) can be written as java statements.

JAWA versus Velocity

The purpose of any template language is to add dynamic capabilities 
like looping, conditionals, and variable expansion to plain old 
static html. Velocity does this by providing a custom-designed 
languague, more restrictive than java, that operates mainly at run 
time. JWAA does the same thing at compile time by providing an excape 
sequence through which Java is the html extension language. In this 
respect, JWAA is similar to JSP, but it differs from either by 
working entirely at compile time.

Since the html and java code are combined at compile time, html 
designers must be trained to type make, and the servlet engine must 
be configured to reload the changed class files. Since errors are 
detected at compile time, html designers must be trained to handle 
compilation errors in their code.

Alternatives Considered

The technique described here is based on a decade of experience 
building large web applications in Perl and JSP. This was followed by 
six months experience with using JWAA in the conventional manner, by 
coding views and controllers into the same class, which is still my 
preferred way of working.

But since it is clear most shops do not work this way, I spent 
several days examining how to separate presentation and control into 
different files within JWAA.

The first approach was to literally integrate Velocity and JWAA, by 
modifying Velocity's VelocityServlet to use JWAA's technique for 
dispatching to Pages controllers as before. I modified the controller 
API to return, not void, but either null (if the controller emits 
text internally, as today) or an instance of View for processing by 
Velocity.

Although this approach worked out well, I soon abandoned the whole 
idea in favor of the one described above. I document the reasons here 
in the hope that designers of similar systems will avoid making the 
same mistakes that the Velocity designers did.

The first reason is one I've complained about in TheProblem under 
"Misuing dynamic binding" and "Configurations are not files". I built 
a JWAA controller class, VelocityPage.j, that returned a View 
requesting Velocity to display the file VelocityPageView.vt". 
Velocity threw an exception that said, literally, "Cannot find the 
template file". Precisely that, without mentioning which file 
couldn't be found, nor more usefully, in which directories it was 
examining.

There ensued an in depth investigation of the Velocity source code 
and documentation to discover a fact that should have been obvious 
from the error message. It soon became apparent that Velocity was 
eaten through and through by what I'll call the "properties disease"; 
the belief that "flexibility" is a goal worth striving for and that 
the best means of achieving that goal is to put anything that might 
ever need changing into properties files, resource files, XML files, 
as distinct from "hard-wiring" them into bad old Java code. No doubt 
this notion originated from C, which is certainly hard for most users 
to change. But this doesn't apply at all to Java, particularly since 
users can define abstract classes that override parameter settings as 
easily, and far more reliably, than changing values in properties 
files.

By the end of three days of struggling with the completely trivial 
problem of where my template files should go, I found the answer. You 
specify the pathname of a properties file in web.xml, and scores of 
variables can be overriden as you please. So I defined the variables 
that seemed appropriate, relaunched the application, and whammo; a 
NullPointerException from somewhere deep within Velocity, without 
even a variable name or line number to go by.

Although I like the Velocity language, having to read (or in my case, 
modify) any application's source code to diagnose a simple 
configuration mistake is absolutely unacceptable. Velocity suffers 
from exactly the same myths about the "right" way to conffigure 
applications that have made Apache, Tomcat, JSP, and other apache 
projects so hard to get working right. Life's too short to spend time 
diagnosing NullPointerExceptions in somebody else's code.

By then, I had sufficient confirmation for the suspicion that 
Velocity was using dynamic binding for a problem, configuration, that 
static binding could solve better. This made me wonder if software 
developers and information architect's work could also be bound 
statically too. This page is the outcome of that question.

Future Directions

The include file functionality described here was added very quickly. 
The <<syntax>> was chosen for ease of parsing and could be improved 
on substantially.

A bigger shortcoming is that the html text in each template file must 
be enclosed in a ctx.send({{...}}); wrapper. Various hacks might 
relieve this requirement without losing the ability to use for(), 
while() and if() statements within template files.

A better solution would be to eliminate the reliance on MLS 
{{digraphs}} in template files and to provide a full parser for a 
template language similar to Velocity's. This parser would compile 
Velocity-style statements into Java statements at pre-compile time 
unlike Velocity's runtime approach. If you're interested in working 
on such a project, contact the author.
------------------------------------------------------------------------

The End - Brad Cox

-- 
---
For industrial age goods there were checks and credit cards.
For everything else there is mybank.dom at http://virtualschool.edu/mybank
Brad Cox, PhD; bcox@virtualschool.edu 703 361 4751


Mime
View raw message