July 23, 2003
Here is the much anticipated TilesTool, to use with Velocity Tools and Struts 1.1
I personally use the nested tiles and tile-controllers features, but I haven't tried other features you might be interested in.
Any comments are welcome on the Velocity Developer's List <velocity-dev@jakarta.apache.org>.
Marinó A. Jónsson
{ { {
package org.apache.velocity.tools.struts;
import java.util.Stack;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletContext;
import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;
import org.apache.struts.tiles.ComponentContext;
import org.apache.struts.tiles.ComponentDefinition;
import org.apache.struts.tiles.AttributeDefinition;
import org.apache.struts.tiles.DirectStringAttribute;
import org.apache.struts.tiles.DefinitionAttribute;
import org.apache.struts.tiles.DefinitionNameAttribute;
import org.apache.struts.tiles.PathAttribute;
import org.apache.struts.tiles.TilesUtil;
import org.apache.struts.tiles.DefinitionsFactoryException;
import org.apache.struts.tiles.Controller;
import org.apache.velocity.tools.view.context.ViewContext;
import org.apache.velocity.tools.view.tools.ViewTool;
/**
{{{ * <p>Title: TilesTool</p>
* <p>Description: A tool to use struts-tiles with Velocity</p>
* <p>Usage:
*
* Just call $tiles.name_of_tile_definition from the template to insert
* the tile.
*
* $tiles.getString("name_of_tile_attribute") fetches a named attribute-value
* from the current tiles-context.
*
* @author <a href="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
* @version $Revision: 1.1 $ $Date: 2003/07/24 04:59:21 $
*/ public class TilesTool implements ViewTool {
{{{ protected ViewContext context;
protected ServletContext application; protected HttpServletRequest request; protected HttpServletResponse response; /**
A stack to hold ComponentContexts while nested tile-definitions
- are rendered.
- /
- Default constructor. Tool must be initialized before use.
- /
public TilesTool() {} /**
- Initializes this tool.
@param obj the current ViewContext
@throws IllegalArgumentException if the param is not a ViewContext
- /
if (!(obj instanceof ViewContext)) {
throw new IllegalArgumentException("Tool can only be initialized with a ViewContext");
this.context = (ViewContext)obj; this.request = context.getRequest(); this.response = context.getResponse(); this.application = context.getServletContext();
- Fetches a named attribute value from the current tiles-context.
<p>This is functionally equivalent to
<code><tiles:getAsString name="title" /></code>.</p>
- @param name the name of the tiles-attribute to fetch
- @return the attribute value as String
- /
ComponentContext context = ComponentContext.getContext(request); Object attrValue = context.getAttribute(name); if (attrValue == null) {
- return null;
<p>A generic tiles insert function</p>
<p>This is functionally equivalent to
<code><tiles:insert attribute="menu" /></code>.</p>
- @param attr - can be any of the following:
- tile-definition name,
- tile-attribute name,
- regular uri.
- (checked in that order)
- @return the rendered template or value as a String
- @throws Exception on failure
- /
ComponentContext currentContext = ComponentContext.getContext(request); Object attrValue = currentContext.getAttribute(attr.toString()); if (attrValue != null) {
- return processObjectValue(attrValue);
- Process an object retrieved as a bean or attribute.
- @param value - Object can be a typed attribute, a String, or anything
- else. If typed attribute, use associated type. Otherwise, apply
- toString() on object, and use returned string as a name.
- @throws Exception - Throws by underlying nested call to
- processDefinitionName()
- @return the fully processed value as String
- /
if (value instanceof AttributeDefinition) {
return processTypedAttribute((AttributeDefinition)value);
else if (value instanceof ComponentDefinition) {
return processDefinition((ComponentDefinition)value);
return processAsDefinitionOrURL(value.toString());
- Process typed attribute according to its type.
- @param value Typed attribute to process.
- @return the fully processed attribute value as String.
- @throws Exception - Throws by underlying nested call to processDefinitionName()
- /
protected String processTypedAttribute(AttributeDefinition value) throws Exception {
if (value instanceof DirectStringAttribute) {
- return (String)value.getValue();
else if (value instanceof DefinitionAttribute) {
return processDefinition((ComponentDefinition)value.getValue());
else if (value instanceof DefinitionNameAttribute) {
- return processAsDefinitionOrURL((String)value.getValue());
return doInsert((String)value.getValue(), null, null);
- Try to process name as a definition, or as an URL if not found.
- @param name Name to process.
- @return the fully processed definition or URL
- @throws Exception
- /
- try {
ComponentDefinition definition =
TilesUtil.getDefinition(name, request, application);
- return processDefinition(definition);
catch (DefinitionsFactoryException ex) {
return processUrl(name);
- End of Process for definition.
- @param definition Definition to process.
- @return the fully processed definition.
@throws Exception from InstantiationException Can't create requested controller
- /
protected String processDefinition(ComponentDefinition definition) throws Exception {
- Controller controller = null; try {
- controller = definition.getOrCreateController(); String role = definition.getRole(); String page = definition.getTemplate(); return doInsert(definition.getAttributes(),
- page, role, controller);
catch (InstantiationException ex) {
- throw new Exception(ex.getMessage());
- controller = definition.getOrCreateController(); String role = definition.getRole(); String page = definition.getTemplate(); return doInsert(definition.getAttributes(),
- Processes an url
- @param url the URI to process.
- @return the rendered template as String.
- @throws Exception
- /
- return doInsert(url, null, null);
- Use this if there is no nested tile.
- @param page the page to process.
- @param role possible user-role
- @param controller possible tiles-controller
- @return the rendered template as String.
- @throws Exception
- /
if (role != null && !request.isUserInRole(role)) {
- return null;
ComponentContext subCompContext = new ComponentContext(); return doInsert(subCompContext, page, role, controller);
- Use this if there is a nested tile.
- @param attributes attributes for the sub-context
- @param page the page to process.
- @param role possible user-role
- @param controller possible tiles-controller
- @return the rendered template as String.
- @throws Exception
- /
- String page, String role, Controller controller) throws Exception
if (role != null && !request.isUserInRole(role)) {
- return null;
ComponentContext subCompContext = new ComponentContext(attributes); return doInsert(subCompContext, page, role, controller);
- An extension of the other two doInsert functions
- @param subCompContext the sub-context to set in scope when the
- template is rendered.
- @param page the page to process.
- @param role possible user-role
- @param controller possible tiles-controller
- @return the rendered template as String.
- @throws Exception
- /
protected String doInsert(ComponentContext subCompContext,
- String page, String role, Controller controller) throws Exception
- pushTilesContext(); try {
ComponentContext.setContext(subCompContext, request);
if (controller != null) {
- controller.perform(subCompContext,
- request, response, application);
- controller.perform(subCompContext,
- popTilesContext();
<p>pushes the current tiles context onto the context-stack.
- preserving the context is necessary so that a sub-context can be
put into request scope and lower level tiles can be rendered</p>
- /
- if (contextStack == null) {
- contextStack = new Stack();
contextStack.push(ComponentContext.getContext(request));
<p>pops the tiles sub-context off the context-stack after the lower level
tiles have been rendered</p>
- /
ComponentContext.setContext((ComponentContext)contextStack.pop(), request);
<p>Renders a template</p>
- @param templateName - name of template to be rendered
- @throws Exception if it fails
- @return the rendered template as a String
- /
StringWriter sw = new StringWriter(); Template template = Velocity.getTemplate(templateName); template.merge(context.getVelocityContext(), sw); return sw.toString();
}
{{{ } } }
- }}}