You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tiles.apache.org by HC <ca...@gmail.com> on 2012/08/23 02:30:54 UTC

How can I do "runtime composition" with tiles and spring mvc?

Hi,

I have a the following requirement and am trying to figure out how to make
this happen in spring mvc and tiles.

1. A user should be able to assemble a final page layout by picking or ore
more page elements from a library of page element templates.
2. A user should be able to specify the order of page elements rendered on
the page.

So, I am thinking of having various jsp pages as the page elements and a
master template with a body tiles attribute which will render all the
elements the user chooses to have rendered. I saw tiles has the List
Attribute capability through which I would be able to populate the body
attribute multiple jsps.

http://tiles.apache.org/2.2/framework/tutorial/advanced/list-attributes.html

However, since the user has control over what goes in the page, the tiles
definition happens at runtime by the user. That means I need a runtime way
of expressing what jsps goes into the body tag and in what order.

Tiles seems to have a "runtime composition" capability through the tiles
container API to do the above:
http://tiles.apache.org/2.2/framework/tutorial/advanced/runtime.html

Per the examples I have tried the following in my spring mvc controller. I
have also configured to use a mutable container in my TilesConfigurer.

@RequestMapping(method = RequestMethod.GET, value = "/")
public ModelAndView index(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
 MutableTilesContainer container = (MutableTilesContainer)
ServletUtil.getCurrentContainer(request,
request.getSession().getServletContext());
 ListAttribute tiles = new ListAttribute();
Attribute attr = new Attribute("/WEB-INF/views/element_xyz.jspx");
tiles.add(attr);
 attr = new Attribute("/WEB-INF/views/element_abc.jspx");
tiles.add(attr);
 AttributeContext context = container.startContext(request, response);
context.putAttribute("body", tiles);
 mv.setViewName("universaltask/index");
mv.getModelMap().put("app_name", "My App");
return mv;
}


The above fails with the following error or other very similar messages
depending on the renderer:

org.apache.tiles.impl.InvalidTemplateException: Cannot render a template
that is not a string: [/WEB-INF/views/element_xyz.jspx]
at
org.apache.tiles.renderer.impl.TemplateAttributeRenderer.write(TemplateAttributeRenderer.java:46)
at
org.apache.tiles.renderer.impl.AbstractBaseAttributeRenderer.render(AbstractBaseAttributeRenderer.java:106)
at
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:670)
at
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:336)
at
org.apache.tiles.template.InsertAttributeModel.renderAttribute(InsertAttributeModel.java:210)
at
org.apache.tiles.template.InsertAttributeModel.end(InsertAttributeModel.java:126)
at
org.apache.tiles.jsp.taglib.InsertAttributeTag.doTag(InsertAttributeTag.java:311)
at
org.apache.jsp.WEB_002dINF.layouts.default_jspx._jspx_meth_tiles_insertAttribute_1(org.apache.jsp.WEB_002dINF.layouts.default_jspx:144)
at
org.apache.jsp.WEB_002dINF.layouts.default_jspx._jspService(org.apache.jsp.WEB_002dINF.layouts.default_jspx:70)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:109)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:389)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:486)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:380)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:538)
at
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
at
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:517)
at
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)
at
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:937)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:406)
at
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
at
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:871)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:284)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:115)

Re: How can I do "runtime composition" with tiles and spring mvc?

Posted by Nicolas LE BAS <ma...@nlebas.net>.
Hi,

Please see my answers below:

On 12-08-22 08:30 PM, HC wrote:
> Hi,
> 
> I have a the following requirement and am trying to figure out how to make
> this happen in spring mvc and tiles.
> 
> 1. A user should be able to assemble a final page layout by picking or ore
> more page elements from a library of page element templates.
> 2. A user should be able to specify the order of page elements rendered on
> the page.

[...]

> Tiles seems to have a "runtime composition" capability through the tiles
> container API to do the above:
> http://tiles.apache.org/2.2/framework/tutorial/advanced/runtime.html

Absolutely!

> Per the examples I have tried the following in my spring mvc controller. I
> have also configured to use a mutable container in my TilesConfigurer.

I think you don't need a mutable tiles container (that's for situations
even more weird, basically when you use the tag <tiles:definition>, not
<tiles:insertDefinition> with <tiles:putAttribute>).
Let's keep it as simple as possible.

I also think you shouldn't need nor attempt to use the Java API for
Tiles inside the controller, that looks awkward and hard to maintain (if
you really want to use the API, you should create your own View subclass
in Spring MVC...).

Here is what I would suggest on the spot (I didn't test it, but I think
it should be much easier):

@RequestMapping(...)
public String index(Model model) {
   model.addAttribute("elements", Arrays.asList(
      "/WEB-INF/views/element_xyz.jspx",
      "/WEB-INF/views/element_abc.jspx"));
   return "universaltask/index";
}

and then deal with it in a JSP. Something like:

<tiles:insertDefinition name="elementContainer">
  <tiles:putListAttribute name="elementList">
    <c:forEach var="element" items="${elements}">
      <tiles:addAttribute value="${element}" type="template"/>
    </c:forEach>
  </tiles:putListAttribute>
</tiles:insertDefinition>

or even:
    <c:forEach var="element" items="${elements}">
      <tiles:insertTemplate template="${element}"/>
    </c:forEach>

You may also try and convert the individual elements into Tiles
definitions themselves, instead of template JSPs, and pass some
contextual information through tiles:

<c:forEach var="element" items="${elements}" varStatus="status">
  <tiles:insertDefinition name="${element}">
    <tiles:putAttribute name="environment" value="ShoppingBasket" />
<!-- or value="Order" if we display the element as part of an order
instead of a shopping basket -->
    <tiles:putAttribute name="rownum" value="${status.count}" />
  </tiles:insertDefinition>
</c:forEach>

Does that appear like what you're looking for?

Nick.