A Word About Web Template (FreeMarker) Scripts...

Templates are meant to be simple...

So I've been seeing this a lot recently, so I thought I'd do a quick blog about it...

With web contents, we can build structures to provide an organized chunk of data, and then we can define a template to render the HTML fragment for that structure.

Since the template language of choice is FreeMarker, and since in FreeMarker we have practically the entire Liferay API (and then some) at our fingertips, it is tempting to believe that the world is our oyster, that we can do anything we want to in our templates.

I'm sorry to tell you, friends, that really is not the case...

First, let's get back to the use case - generating the HTML fragment for displaying the structured web content. That's really it. It is for the current web content structure that is being displayed.

It can be tempting to use the various context classes to invoke Liferay services, or even to do things like parse XML or do lookups or conversions, throw in some complex conditional logic to display some things but not others, etc.

And if you do try these things, you'll find that they work. Using a single web content as an example, you can use the template to render the fragment and all seems to be fine.

What you won't see, unless you do actual load testing, are the performance penalties that you're going to be paying... It all comes down to scale...

To render a web content from a template, a FreeMarker context object is created and populated with the default context objects as well as the structure values. Then a FreeMarker engine instance is created to render the content. The template and the context is given to the FM engine, it interprets and runs the template to generate the HTML fragment. Whatever complexity you've coded into the template, well it needs to be executed and aggregated in the fragment. Finally, the HTML fragment is done, the engine is discarded, and the fragment is returned to the web content display (or others) to then include in the page.

If you have, say, 5 of these on your home page, and you have 1,000 visitors render your page concurrently, this translates into the above process happening 5,000 times. If you're doing things like invoking the journal article service to do a search to pull related articles to the current article, you're doing that 5,000 times. If you're creating an XML parser to parse fields out of the web content article, you're doing that 5,000 times.

And what happens if you get slashdotted and have 10,000 or 20,000 visitors? The numbers start to blow up!

And if you're using an asset publisher to get a list of the web contents using the structure, then the template would be invoked for each entry rendering, so that will always be the asset publisher overhead plus X times the individual rendering (where X is the number of articles your AP will display) times the number of concurrent views...

It simply is not sustainable unless you have a huge, beefy, expensive cluster that you don't mind throwing horsepower at...

At the end of the day, web template scripts should adhere to the following rules:

1. Only display content from the article (structure) itself, don't try to retrieve other data. This may mean that you need to include additional fields in the structure, and perhaps you populate them automatically during saves (use a model listener to inject values before persisting) or just force the content editor to add them in, but you want the template to use only what it has, and nothing more.

2. Do nothing complex in the script itself, instead push the logic into a java class, then inject the instance into the FreeMarker context using a context contributor. Anything you do in the FM script is interpreted, so moving complex logic out of the template and into a compiled java class will always offer better performance.

3. When in doubt, strip it out. If you're adding code to a template and wondering if it will have a performance impact, odds are it will and you should just strip it.

4. Load test your templates. Create a page that renders a lot of these templates on a page and hit your portal with a large number of concurrent users. Simulate what you expect to see load-wise for norms, but also hit it with excessive load. You need to know what your capacity is with the templates the way they are, and if you don't load test you'll have no idea what your breaking point is going to be.

That's basically it. If you follow these rules, you shouldn't have any problems with your templates. If you can't follow the rules, well then be aware that there is a price to be paid and at some point in your future, the bill will come due...

Blogs

One thing we do need is a codex of useful template variables and for that list to be robust enough so as to avoid having to do anything complex in a template. My need is different to someone else's, while the basics are there it would be nice for Liferay to provide some servicelocator data more easily.

 

One example I have is in the theme. I want to know if the user is a site admin, a site member or just a visitor. Stop and think how powerful that is, and there is a permissions checker but it's so complex it's a barrier. Just give us isSiteMember etc.

Thanks for this really nice post David! In my freemarker template I was exactly doing what you describe. Creating an XML parser to parse fields out of the web content article. This is because I have never heard about context contributors before. I would definitely try to use a context contributor in the next freemarker template I am working on.