velocity-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Geir Magnusson Jr." <ge...@optonline.net>
Subject Re: Can I write a macro that can call itself?
Date Fri, 13 Jul 2001 03:37:35 GMT
Brad Cox wrote:
> 
> 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 ;)
> 

Brad!  I suspected it was only a matter of time...

'info architect'? :)

Granted, designers should rarely have the urge to recurse, but as
Velocity is a general templating tool, the VMs do support it.  Not all
of those that generate templates are doing it for the web - Velocity is
a powerful tool for general templating, and is used in non-web
applications.  It's even being used in a commercial UML product...

But, alas, you are wrong.  Velocity's Velocimacros can indeed recurse...

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

Hm.  Ok - as long as we constrain the discussion to web app
development...  after that, this too is wrong, as Velocity makes no
demands that the non-VTL content is HTML....

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

So far, so good.   I will note that it's not just in Velocity, but any
conventional Model2-like web application that utilizes a front
controller and beans or bean-like objects to provide the dynamic
content.
 
> 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).

That sounds like the same approach Lutris' XMLC takes...  there are
enough lurkers on this list with XMLC expertise to comment...
 
On the size issue, not clear that is important on a general web
application server.  In an embedded system, that's fantastic...

Now, from a developers standpoint, it's the same thing, right?  Only at
compile time do you bind the whole pile into one.  (Like JSP?)

Any chance of providing real numbers to document speed, size and
security?  Where does the security come from?  With a Model2 pattern,
the security is guaranteed by the front controller...

 
> 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).

Compile-time type-checking is at best a preference.  Velocity provides
the ability for data objects to simply support the appropriate methods -
strict type has nothing to do with it - and that is determined at run
time.  A useful feature, I claim...

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

How does this work out for deployment?  How do you know if all the
pieces made it to the production server?
 
> 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.

Then there is no dynamicism to the structure of the page at runtime? 
What I mean is, since you seem to imply that at compile time all
decisions are made regarding the structure of the view output, then it
appears I must either  produce N different 'pages' in JAWA to correspond
to N decisions I can make about layout, or not bother.

How would you 'skin' - making a choice about page body at runtime based
on user or other instance information?


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

This is a plus?
 
> Alternatives Considered
> 
> The technique described here is based on a decade of experience
> building large web applications in Perl and JSP.

I would assume most of that decade was in Perl :) 

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

Any time spent working with Velocity, or it's predecessor, WebMacro?

I guess it's preference - the advantage of external views is that they
can be easily added w/o having to modify the Java....
 
> 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.

Similar to post 1.0 VelocityServlet, where a null return of
handleRequest() implies that the request was handled internally, and no
template rendering is needed...


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

Sorry - I am a little tired... long week.  What exactly are those
mistakes?

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

That's good feedback although it appears that there is something odd
with your development pattern - I assume that since you passed a string
to getTemplate(), you might indeed remember what template you were
asking for when you catch the exception.  I assume you believe it would
somehow be easier if you had to parse or manipulate the exception to
find out the name of the template (which you specified in the
getTemplate() method) you couldn't find?

Also, you the application setup the search path - why does velocity have
to regurgitate the search path for you on each failed request?  You can
always ask Velocity for the search path...
 
> 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. 

Or, you could have looked back into your own code that was settingup the
engine to discover what path you set up in the first place...

Or you could have looked at the examples....

Or you could have asked a helpful and eager community for help...

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

Hm.  In terms of resources, which what seems to challenge you, we have
found that it is a very flexible system...  that's why the creation of
new loaders is so trivial and easy - they just plug in.  Velocity will
read templates from files, but also from jars, the classpath, and even
DataSources, if you are so inclined (and a user was and donated to the
project...)

> No doubt
> this notion originated from C, which is certainly hard for most users
> to change.

No, I think it originated from the idea that different users have
different requirements, and different preferences.  For example,
Velocity is used differently when developing vs production - in
development, people generally dont' cache templates for immediate
feedback.  I guess when you have to compile and deploy in JAWA, that
kind of thing doesn't matter....

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

Again, that means you have to rebuild when you change a setting.  In my
experience, I would prefer if operations activities do *not* require
operations staff to define abstract classes and recompile and
redeploy...  To be frank, I think it's a dumb idea in the real world... 
might work in a hypothetical or academic construction, but in real life,
that doesn't happen in my experience.

Ok - lets assume the above has some valid examples behind it.  Would you
like to elucidate so we can either debunk your misconceptions, or learn
from what you are saying and fix it?

> By the end of three days of struggling with the completely trivial
> problem of where my template files should go, I found the answer. 

(Please let it be "I read the manual...")

> You
> specify the pathname of a properties file in web.xml, and scores of
> variables can be overriden as you please.

This is not even wrong.  I worry that it took three days to figure this
out, as there are two separate examples provided with the distribution
that show two separate ways of setting things up.  The VelocityServlet
is a convenience class provided with the general template engine, and
there certainly are improvements to the init strategy depending upon
your needs that users take.

Yes, the common way is to use the web.xml webapp deployment descriptor
to pass an init parameter to the servlet.  However, Velocity makes
absolutely no demands upon the programmer that this is how it should or
must be done - you can hardwire them, get them from a JNDI source, etc,
etc, etc.

Further, for production deployment, you can use a resource loader that
gets templates from jars, or a database, or write a custom one that does
something different...

Next time, just ask on the list.  There are plenty of people ready to
help you when you have a problem...

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

Ok - this is something very, very serious.   This should never happen. 
If you would let us know the configuration that caused that, I would be
very interested to fix immediately.  I am guessing what it was and will
investigate..
 
> 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. 

Brad, you are grandstanding here in a community of volunteers for your
own promotion....  if you had simply asked, it could have been explained
and/or fixed...

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

Yah.  Next time ask...

And in real production, Apache is the worlds most popular web
server....  it's not that hard... :)
 
> 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.

Ok.  Strawman is now set up... moving on to solution?
 
> 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.

I have to admit I was interested to see you engaging in Velocity - it
made me realize that we are getting on the radar in a big way. 

However, I will admit I am disappointed in the approach you took - you
had a problem, didn't ask anyone for information, seemingly won't reveal
the problem, and are using the above to promote something which so far
has no claimed advantages over Velocity other than I assume that you
personally find it easier to set up...

Sorry if I am getting snappy, but next time you wish to use the Velocity
user list to advertise one of your projects, please provide a clearer
problem.   Your biggest problem with Velocity seems to be the problems
you had with configuration,  and as I read it, you didn't present any
clear advantage of JAWA over Velocity for  working developers.

I am happy to continue the thread - take your best shot at Velocity, but
please provide factual examples of problems.  If they are real, we'll
fix them.  If they are strawmen, we'll burn them...

Also I am personally very interested in any type of Java-based View
technology, so if you can clearly describe JAWA and what it offers, I am
glad to participate in that discussion.

geir

-- 
Geir Magnusson Jr.                           geirm@optonline.net
System and Software Consulting
Developing for the web?  See http://jakarta.apache.org/velocity/
You have a genius for suggesting things I've come a cropper with!

Mime
View raw message