This project has retired. For details please refer to its Attic page.
TilesDecorationFilter xref
View Javadoc

1   /*
2    * $Id: TilesDecorationFilter.java 616890 2008-01-30 20:16:51Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.tiles.web.util;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.tiles.Attribute;
26  import org.apache.tiles.AttributeContext;
27  import org.apache.tiles.TilesContainer;
28  import org.apache.tiles.TilesException;
29  import org.apache.tiles.Attribute.AttributeType;
30  import org.apache.tiles.access.TilesAccess;
31  
32  import javax.servlet.Filter;
33  import javax.servlet.FilterChain;
34  import javax.servlet.FilterConfig;
35  import javax.servlet.ServletContext;
36  import javax.servlet.ServletException;
37  import javax.servlet.ServletRequest;
38  import javax.servlet.ServletResponse;
39  import javax.servlet.http.HttpServletRequest;
40  import java.io.IOException;
41  import java.util.Map;
42  import java.util.Enumeration;
43  import java.util.HashMap;
44  
45  /***
46   * Decoration Filter.  Intercepts all requests and decorates them
47   * with the configured definition.
48   * <p/>
49   * For example, given the following config:
50   * <xmp>
51   * <filter>
52   * <filter-name>Tiles Decoration Filter</filter-name>
53   * <filter-class>org.apache.tiles.web.TilesDecorationFilter</filter-class>
54   * <init-param>
55   * <param-name>definition</param-name>
56   * <param-value>test.definition</param-value>
57   * </init-param>
58   * <init-param>
59   * <param-name>attribute-name</param-name>
60   * <param-value>body</param-value>
61   * </init-param>
62   * <init-param>
63   * <param-name>prevent-token</param-name>
64   * <param-value>layout</param-value>
65   * </init-param>
66   * </filter>
67   * <p/>
68   * <filter-mapping>
69   * <filter-name>Tiles Decoration Filter</filter-name>
70   * <url-pattern>/testdecorationfilter.jsp</url-pattern>
71   * <dispatcher>REQUEST</dispatcher>
72   * </filter-mapping>
73   * </xmp>
74   * The filter will intercept all requests to the indicated url pattern
75   * store the initial request path as the "body"  attribute and then render the
76   * "test.definition" definition.  The filter will only redecorate those requests
77   * which do not contain the request attribute associated with the prevent token
78   * "layout".
79   */
80  public class TilesDecorationFilter implements Filter {
81  
82      /***
83       * The logging object.
84       */
85      private static final Log LOG =
86          LogFactory.getLog(TilesDecorationFilter.class);
87  
88      /***
89       * Filter configuration.
90       */
91      private FilterConfig filterConfig;
92  
93      /***
94       * The name of the definition attribute used to
95       * pass on the request.
96       */
97      private String definitionAttributeName = "content";
98  
99      /***
100      * The definition name to use.
101      */
102     private String definitionName = "layout";
103 
104     /***
105      * Token used to prevent re-decoration of requests.
106      * This token is used to prevent infinate loops on
107      * filters configured to match wildcards.
108      */
109     private String preventDecorationToken;
110 
111     /***
112      * Stores a map of the type "mask -> definition": when a definition name
113      * mask is identified, it is substituted with the configured definition.
114      */
115     private Map<String, String> alternateDefinitions;
116 
117     /***
118      * The object that will mutate the attribute context so that it uses
119      * different attributes.
120      */
121     private AttributeContextMutator mutator = null;
122 
123     /***
124      * Returns the filter configuration object.
125      *
126      * @return The filter configuration.
127      */
128     public FilterConfig getFilterConfig() {
129         return filterConfig;
130     }
131 
132     /***
133      * Returns the servlet context.
134      *
135      * @return The servlet context.
136      */
137     public ServletContext getServletContext() {
138         return filterConfig.getServletContext();
139     }
140 
141     /*** {@inheritDoc} */
142     public void init(FilterConfig config) throws ServletException {
143         filterConfig = config;
144         String temp = config.getInitParameter("attribute-name");
145         if (temp != null) {
146             definitionAttributeName = temp;
147         }
148 
149         temp = config.getInitParameter("definition");
150         if (temp != null) {
151             definitionName = temp;
152         }
153 
154         temp = config.getInitParameter("prevent-token");
155         preventDecorationToken = "org.apache.tiles.decoration.PREVENT:"+(temp == null ? definitionName : temp);
156 
157         alternateDefinitions = parseAlternateDefinitions();
158 
159         temp = config.getInitParameter("mutator");
160         if (temp != null) {
161             try {
162                 mutator = (AttributeContextMutator) Class.forName(temp)
163                         .newInstance();
164             } catch (Exception e) {
165                 throw new ServletException("Unable to instantiate specified context mutator.", e);
166             }
167         } else {
168             mutator = new DefaultMutator();
169         }
170     }
171 
172     /***
173      * Creates the alternate definitions map, to map a mask of definition names
174      * to a configured definition.
175      *
176      * @return The alternate definitions map.
177      */
178     @SuppressWarnings("unchecked")
179     protected Map<String, String> parseAlternateDefinitions() {
180         Map<String, String> map = new HashMap<String, String>();
181         Enumeration<String> e = filterConfig.getInitParameterNames();
182         while (e.hasMoreElements()) {
183             String parm = e.nextElement();
184             if (parm.startsWith("definition(") && parm.endsWith("*)")) {
185                 String value = filterConfig.getInitParameter(parm);
186                 String mask = parm.substring("definition(".length());
187                 mask = mask.substring(0, mask.lastIndexOf("*)"));
188                 map.put(mask, value);
189                 LOG.info("Mapping all requests matching '" + mask
190                         + "*' to definition '" + value + "'");
191             }
192         }
193         return map;
194     }
195 
196     /*** {@inheritDoc} */
197     public void destroy() {
198         filterConfig = null;
199     }
200 
201 
202     /***
203      * {@inheritDoc}
204      */
205     public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
206             throws IOException, ServletException {
207         // If the request contains the prevent token, we must not reapply the definition.
208         // This is used to ensure that filters mapped to wild cards do not infinately
209         // loop.
210         if (isPreventTokenPresent(req)) {
211             filterChain.doFilter(req, res);
212             return;
213         }
214         
215         TilesContainer container = TilesAccess.getContainer(getServletContext());
216         mutator.mutate(container.getAttributeContext(req, res), req);
217         try {
218             if(preventDecorationToken != null) {
219                 req.setAttribute(preventDecorationToken, Boolean.TRUE);
220             }
221             String definitionName = getDefinitionForRequest(req);
222             container.render(definitionName, req, res);
223         }
224         catch (TilesException e) {
225             throw new ServletException("Error wrapping jsp with tile definition. "
226                             + e.getMessage(), e);
227         }
228     }
229 
230     /***
231      * Returns the final definition to render for the given request.
232      *
233      * @param request The request object.
234      * @return The final definition name.
235      */
236     private String getDefinitionForRequest(ServletRequest request) {
237         if (alternateDefinitions.size() < 1) {
238             return definitionName;
239         }
240         String base = getRequestBase(request);
241         for (Map.Entry<String, String> pair : alternateDefinitions.entrySet()) {
242             if (base.startsWith(pair.getKey())) {
243                 return pair.getValue();
244             }
245         }
246         return definitionName;
247     }
248 
249     /***
250      * Returns the request base, i.e. the the URL to calculate all the relative
251      * paths.
252      *
253      * @param request The request object to use.
254      * @return The request base.
255      */
256     private String getRequestBase(ServletRequest request) {
257         // Included Path
258         String include = (String) request.getAttribute("javax.servlet.include.servlet_path");
259         if (include != null) {
260             return include;
261         }
262 
263         // As opposed to includes, if a forward occurs, it will update the servletPath property
264         // and include the original as the request attribute.
265         return ((HttpServletRequest) request).getServletPath();
266     }
267 
268     /***
269      * The default attribute context mutator to use.
270      */
271     class DefaultMutator implements AttributeContextMutator {
272 
273         /*** {@inheritDoc} */
274         public void mutate(AttributeContext ctx, ServletRequest req) {
275             Attribute attr = new Attribute();
276             attr.setType(AttributeType.TEMPLATE);
277             attr.setValue(getRequestBase(req));
278             ctx.putAttribute(definitionAttributeName, attr);
279         }
280     }
281 
282     private boolean isPreventTokenPresent(ServletRequest request) {
283         return preventDecorationToken != null && request.getAttribute(preventDecorationToken) != null;
284     }
285 }