A few months ago I wrote about integrating Spring, Velocity and Tiles. I discovered that one bit was missing from there: Velocity Tools. Two hours of yak shaving, frantic googling and source reading later, I figured out how to add support for Velocity Tools to such project with no XML configuration. Here’s how.
For starters, let’s say I want to use some tools in my Velocity and Tiles pages. Let’s add the LinkTool
.
template.vm:
<html> <head><title>#tiles_insertAttribute({"name":"title"})#end</title></head> <body> #tiles_insertAttribute({"name":"body"})#end <p>Spring macros work in tiles template, too: #springUrl("/myUrl")</p> <p>Do Velocity tools work in template? $link.contextPath</p> </body> </html>
body.vm:
<p>Here's a demonstration that Spring macros work with Tiles: #springUrl("/myUrl")</p> <p>Do Velocity tools work in Tile? $link.contextPath</p>
When I render the code from previous post, I get this:
Here's a demonstration that Spring macros work with Tiles: /SpringVelocityTiles/myUrl Do Velocity tools work in Tile? $link.contextPath Spring macros work in tiles template, too: /SpringVelocityTiles/myUrl Do Velocity tools work in template? $link.contextPath
Not good.
After some googling, I found a similar question on StackOverflow. It had two helpful answers – one from serg, delegating to this blog post, and another from Scott.
None of them worked out of the box, though. I’m tired of XML configs, and apparently it’s too easy to get weird exceptions related to some Struts tools. No wonder I get them, I don’t use Struts and don’t want any of its tools!
Apparently the issue is that Spring support for Velocity Tools is rubbish. One way out is to write your own ViewResolver
or View
, and that’s what I did in the end.
For starters, I’ll configure my ViewResolver
to use a new view class:
@Bean public ViewResolver viewResolver() { VelocityViewResolver resolver = new VelocityViewResolver(); resolver.setViewClass(MyVelocityToolboxView.class); resolver.setSuffix(".vm"); return resolver; }
MyVelocityToolboxView
is below. This time I’m pasting it with imports to avoid ambiguity on names like Context
or VelocityView
.
package pl.squirrel.svt; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.velocity.context.Context; import org.apache.velocity.tools.Scope; import org.apache.velocity.tools.ToolboxFactory; import org.apache.velocity.tools.config.ConfigurationUtils; import org.apache.velocity.tools.view.ViewToolContext; import org.springframework.web.servlet.view.velocity.VelocityView; public class MyVelocityToolboxView extends VelocityView { @Override protected Context createVelocityContext(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { ViewToolContext context = new ViewToolContext(getVelocityEngine(), request, response, getServletContext()); ToolboxFactory factory = new ToolboxFactory(); factory.configure(ConfigurationUtils.getVelocityView()); for (String scope : Scope.values()) { context.addToolbox(factory.createToolbox(scope)); } if (model != null) { for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) model .entrySet()) { context.put(entry.getKey(), entry.getValue()); } } return context; } }
It’s important that we only use ConfigurationUtils.getVelocityView()
– it includes generic tools and view tools, but not Struts tools.
That’s it, now we have a project which uses Tiles for high-level templating, Velocity for individual pages and details, with (hopefully) full support for Spring macros and Velocity tools in all areas. Even if you don’t like Tiles, it may still serve as a good example of how to integrate Spring and Velocity Tools.
I pushed updated code for the demo application to my GitHub repository.
In the irregular habit of posting a sermon on Friday… Less than a week ago I saw an excellent presentation on using Clojure for Spring views. Compared to all this mess and yak shaving here, that Clojure solution is infinitely simpler, more elegant and more powerful at the same time. Too bad it does not have the market share of Spring & Velocity yet.
Getting compilation error in #tiles_insertAttribute({“name”:”title”})#end
(Encountered “#end\r\n” at form_layout.vm[line 10, column 42])
Please let me know the solution.Thanks in advance for help