1/*2 * $Id: TilesDecorationFilter.java 1058093 2011-01-12 11:49:02Z apetrelli $3 *4 * Licensed to the Apache Software Foundation (ASF) under one5 * or more contributor license agreements. See the NOTICE file6 * distributed with this work for additional information7 * regarding copyright ownership. The ASF licenses this file8 * to you under the Apache License, Version 2.0 (the9 * "License"); you may not use this file except in compliance10 * with the License. You may obtain a copy of the License at11 *12 * http://www.apache.org/licenses/LICENSE-2.013 *14 * Unless required by applicable law or agreed to in writing,15 * software distributed under the License is distributed on an16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY17 * KIND, either express or implied. See the License for the18 * specific language governing permissions and limitations19 * under the License.20 */21package org.apache.tiles.web.util;
2223import java.io.IOException;
24import java.util.Enumeration;
25import java.util.HashMap;
26import java.util.Map;
2728import javax.servlet.Filter;
29import javax.servlet.FilterChain;
30import javax.servlet.FilterConfig;
31import javax.servlet.ServletContext;
32import javax.servlet.ServletException;
33import javax.servlet.ServletResponse;
34import javax.servlet.http.HttpServletRequest;
35import javax.servlet.http.HttpServletResponse;
3637import org.apache.tiles.Attribute;
38import org.apache.tiles.AttributeContext;
39import org.apache.tiles.TilesContainer;
40import org.apache.tiles.access.TilesAccess;
41import org.apache.tiles.request.ApplicationContext;
42import org.apache.tiles.request.Request;
43import org.apache.tiles.request.reflect.CannotInstantiateObjectException;
44import org.apache.tiles.request.reflect.ClassUtil;
45import org.apache.tiles.request.servlet.ServletRequest;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
4849/**50 * Decoration Filter. Intercepts all requests and decorates them51 * with the configured definition.52 * <p/>53 * For example, given the following config:54 * <xmp>55 * <filter>56 * <filter-name>Tiles Decoration Filter</filter-name>57 * <filter-class>org.apache.tiles.web.TilesDecorationFilter</filter-class>58 * <init-param>59 * <param-name>definition</param-name>60 * <param-value>test.definition</param-value>61 * </init-param>62 * <init-param>63 * <param-name>attribute-name</param-name>64 * <param-value>body</param-value>65 * </init-param>66 * <init-param>67 * <param-name>prevent-token</param-name>68 * <param-value>layout</param-value>69 * </init-param>70 * </filter>71 * <p/>72 * <filter-mapping>73 * <filter-name>Tiles Decoration Filter</filter-name>74 * <url-pattern>/testdecorationfilter.jsp</url-pattern>75 * <dispatcher>REQUEST</dispatcher>76 * </filter-mapping>77 * </xmp>78 * The filter will intercept all requests to the indicated url pattern79 * store the initial request path as the "body" attribute and then render the80 * "test.definition" definition. The filter will only redecorate those requests81 * which do not contain the request attribute associated with the prevent token82 * "layout".83 */84publicclassTilesDecorationFilterimplements Filter {
8586/**87 * Init parameter to define the key of the container to use.88 *89 * @since 2.1.290 */91publicstaticfinal String CONTAINER_KEY_INIT_PARAMETER =
92"org.apache.tiles.web.util.TilesDecorationFilter.CONTAINER_KEY";
9394/**95 * The logging object.96 */97private Logger log = LoggerFactory.getLogger(TilesDecorationFilter.class);
9899/**100 * Filter configuration.101 */102private FilterConfig filterConfig;
103104/**105 * The key under which the container is stored.106 */107private String containerKey;
108109/**110 * The name of the definition attribute used to111 * pass on the request.112 */113private String definitionAttributeName = "content";
114115/**116 * The definition name to use.117 */118private String definitionName = "layout";
119120/**121 * Token used to prevent re-decoration of requests.122 * This token is used to prevent infinate loops on123 * filters configured to match wildcards.124 */125private String preventDecorationToken;
126127/**128 * Stores a map of the type "mask -> definition": when a definition name129 * mask is identified, it is substituted with the configured definition.130 */131private Map<String, String> alternateDefinitions;
132133/**134 * The object that will mutate the attribute context so that it uses135 * different attributes.136 */137privateAttributeContextMutator mutator = null;
138139/**140 * The servlet context.141 */142private ServletContext servletContext;
143144/** {@inheritDoc} */145publicvoid init(FilterConfig config) throws ServletException {
146 filterConfig = config;
147 servletContext = filterConfig.getServletContext();
148149 containerKey = filterConfig
150 .getInitParameter(CONTAINER_KEY_INIT_PARAMETER);
151152 String temp = config.getInitParameter("attribute-name");
153if (temp != null) {
154 definitionAttributeName = temp;
155 }
156157 temp = config.getInitParameter("definition");
158if (temp != null) {
159 definitionName = temp;
160 }
161162 temp = config.getInitParameter("prevent-token");
163 preventDecorationToken = "org.apache.tiles.decoration.PREVENT:"164 + (temp == null ? definitionName : temp);
165166 alternateDefinitions = parseAlternateDefinitions();
167168 temp = config.getInitParameter("mutator");
169if (temp != null) {
170try {
171 mutator = (AttributeContextMutator) ClassUtil.instantiate(temp);
172 } catch (CannotInstantiateObjectException e) {
173thrownew ServletException("Unable to instantiate specified context mutator.", e);
174 }
175 } else {
176 mutator = newDefaultMutator();
177 }
178 }
179180/**181 * Creates the alternate definitions map, to map a mask of definition names182 * to a configured definition.183 *184 * @return The alternate definitions map.185 */186 @SuppressWarnings("unchecked")
187protected Map<String, String> parseAlternateDefinitions() {
188 Map<String, String> map = new HashMap<String, String>();
189 Enumeration<String> e = filterConfig.getInitParameterNames();
190while (e.hasMoreElements()) {
191 String parm = e.nextElement();
192if (parm.startsWith("definition(") && parm.endsWith("*)")) {
193 String value = filterConfig.getInitParameter(parm);
194 String mask = parm.substring("definition(".length());
195 mask = mask.substring(0, mask.lastIndexOf("*)"));
196 map.put(mask, value);
197 log.info("Mapping all requests matching '" + mask
198 + "*' to definition '" + value + "'");
199 }
200 }
201return map;
202 }
203204/** {@inheritDoc} */205publicvoid destroy() {
206 filterConfig = null;
207 }
208209210/**211 * {@inheritDoc}212 */213publicvoid doFilter(javax.servlet.ServletRequest req, ServletResponse res, FilterChain filterChain)
214throws IOException, ServletException {
215// If the request contains the prevent token, we must not reapply the definition.216// This is used to ensure that filters mapped to wild cards do not infinately217// loop.218if (!isPreventTokenPresent(req)) {
219 ApplicationContext applicationContext = org.apache.tiles.request.servlet.ServletUtil
220 .getApplicationContext(servletContext);
221 Request request = new ServletRequest(applicationContext,
222 (HttpServletRequest) req, (HttpServletResponse) res);
223TilesContainer container = TilesAccess.getContainer(applicationContext,
224 containerKey);
225 mutator.mutate(container.getAttributeContext(request), req);
226if (preventDecorationToken != null) {
227 req.setAttribute(preventDecorationToken, Boolean.TRUE);
228 }
229 String definitionName = getDefinitionForRequest(req);
230 container.render(definitionName, request);
231 }
232 filterChain.doFilter(req, res);
233 }
234235/**236 * Returns the final definition to render for the given request.237 *238 * @param request The request object.239 * @return The final definition name.240 */241private String getDefinitionForRequest(javax.servlet.ServletRequest request) {
242if (alternateDefinitions.size() < 1) {
243return definitionName;
244 }
245 String base = getRequestBase(request);
246for (Map.Entry<String, String> pair : alternateDefinitions.entrySet()) {
247if (base.startsWith(pair.getKey())) {
248return pair.getValue();
249 }
250 }
251return definitionName;
252 }
253254/**255 * Returns the request base, i.e. the the URL to calculate all the relative256 * paths.257 *258 * @param request The request object to use.259 * @return The request base.260 */261private String getRequestBase(javax.servlet.ServletRequest request) {
262// Included Path263 String include = (String) request.getAttribute("javax.servlet.include.servlet_path");
264if (include != null) {
265return include;
266 }
267268// As opposed to includes, if a forward occurs, it will update the servletPath property269// and include the original as the request attribute.270return ((HttpServletRequest) request).getServletPath();
271 }
272273/**274 * The default attribute context mutator to use.275 */276classDefaultMutatorimplementsAttributeContextMutator {
277278/** {@inheritDoc} */279publicvoid mutate(AttributeContext ctx, javax.servlet.ServletRequest req) {
280Attribute attr = newAttribute();
281 attr.setRenderer("template");
282 attr.setValue(getRequestBase(req));
283 ctx.putAttribute(definitionAttributeName, attr);
284 }
285 }
286287/**288 * Checks if the prevent evaluation token is present.289 *290 * @param request The HTTP request object.291 * @return <code>true</code> if the token is present.292 */293privateboolean isPreventTokenPresent(javax.servlet.ServletRequest request) {
294return preventDecorationToken != null && request.getAttribute(preventDecorationToken) != null;
295 }
296 }