blob: 9b0267b7f92436c3efe32e1ff0569b36ff908c8e [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.tiles.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tiles.Attribute;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
import org.apache.tiles.access.TilesAccess;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.Enumeration;
import java.util.HashMap;
/**
* Decoration Filter. Intercepts all requests and decorates them
* with the configured definition.
* <p/>
* For example, given the following config:
* <xmp>
* <filter>
* <filter-name>Tiles Decoration Filter</filter-name>
* <filter-class>org.apache.tiles.web.TilesDecorationFilter</filter-class>
* <init-param>
* <param-name>definition</param-name>
* <param-value>test.definition</param-value>
* </init-param>
* <init-param>
* <param-name>attribute-name</param-name>
* <param-value>body</param-value>
* </init-param>
* </filter>
* <p/>
* <filter-mapping>
* <filter-name>Tiles Decoration Filter</filter-name>
* <url-pattern>/testdecorationfilter.jsp</url-pattern>
* <dispatcher>REQUEST</dispatcher>
* </filter-mapping>
* </xmp>
* The filter will intercept all requests to the indicated url pattern
* store the initial request path as the "body" attribute and then render the
* "test.definition" definition.
*/
public class TilesDecorationFilter implements Filter {
/**
* The logging object.
*/
private static final Log LOG =
LogFactory.getLog(TilesDecorationFilter.class);
/**
* Filter configuration.
*/
private FilterConfig filterConfig;
/**
* The name of the definition attribute used to
* pass on the request.
*/
private String definitionAttributeName = "content";
/**
* The definition name to use.
*/
private String definitionName = "layout";
/**
* Stores a map of the type "mask -> definition": when a definition name
* mask is identified, it is substituted with the configured definition.
*/
private Map<String, String> alternateDefinitions;
/**
* The object that will mutate the attribute context so that it uses
* different attributes.
*/
private AttributeContextMutator mutator = null;
/**
* Returns the filter configuration object.
*
* @return The filter configuration.
*/
public FilterConfig getFilterConfig() {
return filterConfig;
}
/**
* Returns the servlet context.
*
* @return The servlet context.
*/
public ServletContext getServletContext() {
return filterConfig.getServletContext();
}
/** {@inheritDoc} */
public void init(FilterConfig config) throws ServletException {
filterConfig = config;
String temp = config.getInitParameter("attribute-name");
if (temp != null) {
definitionAttributeName = temp;
}
temp = config.getInitParameter("definition");
if (temp != null) {
definitionName = temp;
}
alternateDefinitions = parseAlternateDefinitions();
temp = config.getInitParameter("mutator");
if(temp != null) {
try {
mutator = (AttributeContextMutator)Class.forName(temp).newInstance();
} catch (Exception e) {
throw new ServletException("Unable to instantiate specified context mutator.", e);
}
} else {
mutator = new DefaultMutator();
}
}
/**
* Creates the alternate definitions map, to map a mask of definition names
* to a configured definition.
*
* @return The alternate definitions map.
*/
@SuppressWarnings("unchecked")
protected Map<String, String> parseAlternateDefinitions() {
Map<String, String> map = new HashMap<String, String>();
Enumeration<String> e = filterConfig.getInitParameterNames();
while(e.hasMoreElements()) {
String parm = e.nextElement();
if(parm.startsWith("definition(") && parm.endsWith("*)")) {
String value = filterConfig.getInitParameter(parm);
String mask = parm.substring("definition(".length());
mask = mask.substring(0, mask.lastIndexOf("*)"));
map.put(mask, value);
LOG.info("Mapping all requests matching '"+mask+"*' to definition '"+value+"'");
}
}
return map;
}
/** {@inheritDoc} */
public void destroy() {
filterConfig = null;
}
/** {@inheritDoc} */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
TilesContainer container = TilesAccess.getContainer(getServletContext());
mutator.mutate(container.getAttributeContext(req, res), req);
try {
String definitionName = getDefinitionForRequest(req);
container.render(definitionName, req, res);
} catch (TilesException e) {
throw new ServletException("Error wrapping jsp with tile definition. "+e.getMessage(), e);
}
}
/**
* Returns the final definition to render for the given request.
*
* @param request The request object.
* @return The final definition name.
*/
private String getDefinitionForRequest(ServletRequest request) {
if(alternateDefinitions.size() < 1) {
return definitionName;
}
String base = getRequestBase(request);
for(Map.Entry<String, String> pair : alternateDefinitions.entrySet()) {
if(base.startsWith(pair.getKey())) {
return pair.getValue();
}
}
return definitionName;
}
/**
* Returns the request base, i.e. the the URL to calculate all the relative
* paths.
*
* @param request The request object to use.
* @return The request base.
*/
private String getRequestBase(ServletRequest request) {
// Included Path
String include = (String) request.getAttribute("javax.servlet.include.servlet_path");
if (include != null) {
return include;
}
// As opposed to includes, if a forward occurs, it will update the servletPath property
// and include the original as the request attribute.
return ((HttpServletRequest) request).getServletPath();
}
/**
* The default attribute context mutator to use.
*/
class DefaultMutator implements AttributeContextMutator {
/** {@inheritDoc} */
public void mutate(AttributeContext ctx, ServletRequest req) {
Attribute attr = new Attribute();
attr.setType(Attribute.TEMPLATE);
attr.setName(definitionAttributeName);
attr.setValue(getRequestBase(req));
ctx.putAttribute(definitionAttributeName, attr);
}
}
}