Why Apache Velocity sucks
Posted on: March 29th, 2011I was just giving Apache Velocity a try because it seems to be the most popular Java template engine on the Internet. I don’t really understand why, as it seems to be completely immature and badly-designed.
References to undefined variables
When you reference a variable $test
in Velocity and this variable is not defined, the string $test
is returned instead. To avoid this (for example in case of optional parameters), you can use the Quiet Reference Notation writing $!test
, in which case an empty string is returned. Stupidly though, this behaviour does not work consistently. When you use the variable as a parameter instead of printing it out for example, $esc.html($!test)
does not output an empty string as expected but instead the string $esc.html($!test)
. Instead, you have to use the notation $!esc.html($test)
. How stupid is that?
To avoid mistakes in your template, you can set the property runtime.references.strict
to true
, in which case undefined references aren’t replaced by their names, but instead an exception is thrown. In that case however, $!test
also throws an exception instead of returning an empty string!
Now, when the variable $test
is defined and you actually want to output the string $test
instead of its value, you do this by writing \$test
instead. This works only when $test
is defined though, and when it is not defined, it outputs \$test
instead of $test
. So depending on whether the variable is defined or not, you have to write either \$test
or $test
to get the string $test
. Things will get very confusing when you are dealing with optional parameters. The funny thing is that every undefined reference produces an error message in the log file, and because of that, there is an official “better” way to do this: to define an own variable that contains the value $
!
Another way is to set the configuration property runtime.references.strict.escape
to true
. In that case, a backslash is also interpreted as an escape character in front of a non-existent reference. Stupidly, this property is (like most other configuration properties as well) only documented in the manual of the most recent development version. Also confusing is its name, as it is only remotely related and in no way a sub-property of runtime.references.strict
.
Output formatting
This code:
<ul>
#foreach( $a in $b )
#if( $a )
<li>$a</li>
#end
#end
</ul>
Will produce the following output (assuming that $b
is a list [1,2,3]
):
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Notice how messed up the indentation is? At least it has to be said that Velocity, in contrast to JSP, is that intelligent that it does not output the newlines of those lines that only contain Velocity directives. But it keeps all the other whitespaces from those lines?!
Documentation
As mentioned before, most configuration properties are only documented in the manual of the most recent development version. When you use the VelocityViewServlet
from the VelocityTools, additionally to the velocity.properties
file, there is a settings.xml
file where you can define global variable that can be referred to in templates. The following important things are missing from the documentation:
- It is described how to create string, number, boolean, list and object variables. However, Velocity also knows Map variables. The documentation does not say how to define these in the
settings.xml
file, it is probably not possible. Also, it does not specify how to define items in a list that have the valuefalse
,n
or similar (as those are converted to booleans) or that contain a comma (as that is the list separator). This is probably also not possible, at least it does not work using a backslash (or, as the documentation sometimes calls it, a “forward slash”). - When defining objects in that file (that is, instances that are created from a given class), you can pass properties to those objects that are either set using setters or using a method called
configure
. The documentation does not mention that there are some predefined properties that you can use. Those would be:servletContext
(javax.servlet.ServletContext
)request
(javax.servlet.http.HttpServletRequest
)response
(javax.servlet.http.HttpServletResponse
)log
(org.apache.velocity.runtime.log.Log
)velocityContext
(org.apache.velocity.tools.view.context.ChainedContext
)velocityEngine
(org.apache.velocity.app.VelocityEngine
)session
(probablyjavax.servlet.http.HttpSession
)key
(java.lang.String
, thekey
of thetool
insettings.xml
)requestPath
(java.lang.String
)scope
(java.lang.String
, thescope
of thetoolbox
insettings.xml
)locale
(java.util.Locale
)
Properties and Methods
Properties in Velocity either refer to a value in a hashtable or to the return value of a getter. Suppose you are working with an object of this class, though:
public class SampleData {
public final int value1;
public final String value2;
}
Trying to access those properties using $sampleData.value1
will not work, instead, the class will have to be added a method getValue1()
(see VELOCITY-12).
Also stupid is that the naming conventions for properties and for methods don’t fit the naming conventions for Java, as those allow a _
and a $
sign in every part of the identifier. This means that properties and methods that start with an underscore or that contain a dollar sign can’t be accessed from Velocity. For gettext, for example, I use a method with the simple name _
(which is quite a common way to use gettext). In order to use Velocity, I will have to change this class now.