FreeMarker vs. JSP 2
I've been doing quite a bit of prototyping with Spring MVC and Struts 2 with both JSP and FreeMarker in the last few months. I'm trying to migrate a proprietary servlet-based framework with a proprietary JSP compiler to something that's open source. There's a couple of important features that the proprietary view framework has:
- It's expression language allows methods to be called with arguments.
- Templates can be loaded from a JAR on a remote server.
- XML in variables is escaped by default.
For #1, I've found this to be impossible with JSP EL or JSTL. I've created JSP functions that allow argument passing, but they don't allow overloading of functions. FreeMarker solves #1.
For #2, JSPs again fail because the templates have to be on the file system or in a WAR. FreeMarker solves this problem as well.
For #3, neither JSP or FreeMarker solve this problem. I realize it can be fixed in FreeMarker by hacking the code - I've done the same with Tomcat and solved it for JSP as well.
So based on the requirements in this project, FreeMarker is the clear winner. Here's some problems that I see with using it:
- No XML escaping of expressions by default
- No compile-time checking of expressions
- IDE support is limited to Eclipse (meaning very little in the way of code-completion)
FreeMarker users - are there other problems you've experienced when using FreeMarker in your applications?
>> FreeMarker users - are there other problems
>> you've experienced when using FreeMarker in your applications?
Yes, those pesky developers who still want to use JSP:)
Posted by Jon Chase on January 17, 2008 at 08:47 PM MST #
The biggest annoyance with FreeMarker (at least when used with Struts2) is that the FreeMarker default when printing a number to the output stream is to print its localized display form (so, for example, if variable foo represents an int value of 1000, it gets written as "1,000" or "1.000" or something else, depending on Locale.
This breaks Javascript, if you've used FreeMarker to insert the value into a JS method, because it doesn't understand that that thing with comma or dot is a number.
It is also When the number is a form value, this is bad because in WebWork this caused the submitted value to not be convertible back to a number automatically. (For example, when the values of a select control are numbers specified by Freemarker variables, and you try to submit the String "1,000" as an int property on the Action, the converter fails. I don't know if that still fails in Struts2 or not--we have ported but we haven't had that issue for a while.
The workaround is to use the FM "c" expression (e.g. $foo?c) when writing the value. It should be the default behavior--you should have to asked for localized display form in my opinion. What sucks about this is that you don't necessarily notice the problem until the numbers get up over 1,000 and the commas start showing up.
On a related note (but not a FM issue), see this bug
Posted by Dennis Doubleday on January 17, 2008 at 09:50 PM MST #
My only nit I ran into recently was I was working with a 3rd party library which extended an AbstractList. When you are working with an element which is a list, it appears you can only get items by index or iterate over it. If you try to call methods (which was why it was extended) - it seemed to barf. (I might be able to work around the issue by playing with the ObjectWrappers more, but did not get that far)
Otherwise - Freemarker is great.
Posted by Funkman on January 18, 2008 at 12:46 AM MST #
@Dennis
I agree.
I understand why FreeMarker was designed to act the way it does, but I think that in day-to-day usage, it ends up being the wrong choice.
The same problem exists with Dates. Yes, I can do ${date?date}, but only if I know that the object I'm printing out is going to be date. I can't just use ${value} to print out a random object (e.g. If I were writing a page that would dump out the contents of the session).
From a "correctness" point of view the implementation is right, but I care about ease of use too, and on that assessment it falls down.
But on the positive side if you do ever print out a ${date}, the error message is incredibly informative.
Posted by Tim Vernum on January 18, 2008 at 02:18 AM MST #
As some may know I'm one of the three guys that could be called "the FreeMarker guys" nowadays. So far I agree with the complains, except the last date thing that I will return to. That said, if I had to design a new template engine now, these things would be inherently fixed.
Regarding the ${date} thing... you *can* write ${date} in FreeMarker, however then the value must be a java.sql.Date or a java.sql.Time or a java.sql.Timestamp. And that's not the mistake of FreeMarker, but of the Java API. Simply, if you have a plain java.util.Date, you can't find out with a program if it's a date, a time or a date-time or what. Sure, you can guess, but not reliably enough. So what is the template engine supposed to do? OK, there could be something like ${value?date_or_bypass}, but them the template author again has to be aware of the issue. Fortunately, people seldom need to print a value without knowing if that's a date or not... as far as I know at least.
What I would agree with is that something like ${value?debug} should be supported, which prints the value *somehow*, even composite stuff like a list, for debugging purposes. But that doesn't help when creating the final production templates... like it's seldom useful to print a list for the visitor as ["apple", "orange"].
Simply, formatting the output is not that trivial as we would like it to be. Any template engine that let you do it trivially is a liar... it does it by making sacrifices in the output quality or otherwise sweeping problems under the carpet. (Like, some popular template engines just doesn't care about formatting numbers for non-US audience, not to mention formatting dates... the just use toString() for everything. Of course it's very simple to use it then... so the user doesn't have to face the fact that there are multiple number formats in use, etc., until you are using non-US number format, or have to print dates for normal visitors. Ops.)
Posted by Dániel Dékány on January 18, 2008 at 02:47 PM MST #
Matt, regarding #1 you should check JEXL.
I have only needed it once, but it was pretty easy to use: just drop in your WAR and start using it (I can't remember if there was anything else, but don't think so). I explicitly checked method calling, and worked as expected.
Can't help with the other issues, though. Good luck.
Posted by Ignacio Coloma on January 19, 2008 at 10:21 AM MST #
Dennis Doubleday: the #{foo} notation does the same thing as foo?c.
Matt: you can assign any escape directive to be used by default inside a tag body, this way:
http://www.freemarker.org/docs/ref_directive_escape.html
Posted by Peter Backlund on January 19, 2008 at 06:38 PM MST #
Posted by Dániel Dékány on January 20, 2008 at 08:24 PM MST #
Posted by Hank on February 24, 2008 at 07:48 AM MST #
Posted by 88.209.218.37 on February 24, 2008 at 02:26 PM MST #
Spring allows a nice and easy way to escape xml:
Also, I agree that the default number format is a problem. In testing, id's of over 1000 never come up. Who would think of that? But because freemarker modifies them, id's passed in query string will automatically get formated to ?personId=1,436.
Because the ids get this high in PRODUCTION code, this causes problems with production software. It feels stupid having to say to your client what the hell happened when you claim to test the crap out of it... and then something unexpected like this happens.
Overall though, Freemarker is much better than JSP.
The only thing it needs is for macros to be considered objects... and have multiple extension points. Maybe this is actually in there... but having only a single <#nested> is not enough. Somtimes I want to plugin different versions of javascript for individual pages and also customize the body while having code in between them. Having an inherited macro would have fit the bill nicely.
Posted by Ken Egervari on September 21, 2009 at 08:24 AM MDT #
Hi,
I just wanted to point out that by using a "freemarker.properties" accessible via the classpath, and the property
defined inside that file, the default format for numbers is forced to the plain regular format mentioned in the mail above.
Posted by Harry Karadimas on July 28, 2010 at 07:54 PM MDT #