Project Tiles has retired. For details please refer to its Attic page.
TilesDecorationFilter xref
View Javadoc

1   /*
2    * $Id: TilesDecorationFilter.java 1058093 2011-01-12 11:49:02Z 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 java.io.IOException;
24  import java.util.Enumeration;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import javax.servlet.Filter;
29  import javax.servlet.FilterChain;
30  import javax.servlet.FilterConfig;
31  import javax.servlet.ServletContext;
32  import javax.servlet.ServletException;
33  import javax.servlet.ServletResponse;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import org.apache.tiles.Attribute;
38  import org.apache.tiles.AttributeContext;
39  import org.apache.tiles.TilesContainer;
40  import org.apache.tiles.access.TilesAccess;
41  import org.apache.tiles.request.ApplicationContext;
42  import org.apache.tiles.request.Request;
43  import org.apache.tiles.request.reflect.CannotInstantiateObjectException;
44  import org.apache.tiles.request.reflect.ClassUtil;
45  import org.apache.tiles.request.servlet.ServletRequest;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * Decoration Filter.  Intercepts all requests and decorates them
51   * with the configured definition.
52   * <p/>
53   * For example, given the following config:
54   * &lt;xmp&gt;
55   * &lt;filter&gt;
56   * &lt;filter-name&gt;Tiles Decoration Filter&lt;/filter-name&gt;
57   * &lt;filter-class&gt;org.apache.tiles.web.TilesDecorationFilter&lt;/filter-class&gt;
58   * &lt;init-param&gt;
59   * &lt;param-name&gt;definition&lt;/param-name&gt;
60   * &lt;param-value&gt;test.definition&lt;/param-value&gt;
61   * &lt;/init-param&gt;
62   * &lt;init-param&gt;
63   * &lt;param-name&gt;attribute-name&lt;/param-name&gt;
64   * &lt;param-value&gt;body&lt;/param-value&gt;
65   * &lt;/init-param&gt;
66   * &lt;init-param&gt;
67   * &lt;param-name&gt;prevent-token&lt;/param-name&gt;
68   * &lt;param-value&gt;layout&lt;/param-value&gt;
69   * &lt;/init-param&gt;
70   * &lt;/filter&gt;
71   * <p/>
72   * &lt;filter-mapping&gt;
73   * &lt;filter-name&gt;Tiles Decoration Filter&lt;/filter-name&gt;
74   * &lt;url-pattern&gt;/testdecorationfilter.jsp&lt;/url-pattern&gt;
75   * &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
76   * &lt;/filter-mapping&gt;
77   * &lt;/xmp&gt;
78   * The filter will intercept all requests to the indicated url pattern
79   * store the initial request path as the "body"  attribute and then render the
80   * "test.definition" definition.  The filter will only redecorate those requests
81   * which do not contain the request attribute associated with the prevent token
82   * "layout".
83   */
84  public class TilesDecorationFilter implements Filter {
85  
86      /**
87       * Init parameter to define the key of the container to use.
88       *
89       * @since 2.1.2
90       */
91      public static final String CONTAINER_KEY_INIT_PARAMETER =
92          "org.apache.tiles.web.util.TilesDecorationFilter.CONTAINER_KEY";
93  
94      /**
95       * The logging object.
96       */
97      private Logger log = LoggerFactory.getLogger(TilesDecorationFilter.class);
98  
99      /**
100      * Filter configuration.
101      */
102     private FilterConfig filterConfig;
103 
104     /**
105      * The key under which the container is stored.
106      */
107     private String containerKey;
108 
109     /**
110      * The name of the definition attribute used to
111      * pass on the request.
112      */
113     private String definitionAttributeName = "content";
114 
115     /**
116      * The definition name to use.
117      */
118     private String definitionName = "layout";
119 
120     /**
121      * Token used to prevent re-decoration of requests.
122      * This token is used to prevent infinate loops on
123      * filters configured to match wildcards.
124      */
125     private String preventDecorationToken;
126 
127     /**
128      * Stores a map of the type "mask -> definition": when a definition name
129      * mask is identified, it is substituted with the configured definition.
130      */
131     private Map<String, String> alternateDefinitions;
132 
133     /**
134      * The object that will mutate the attribute context so that it uses
135      * different attributes.
136      */
137     private AttributeContextMutator mutator = null;
138 
139     /**
140      * The servlet context.
141      */
142     private ServletContext servletContext;
143 
144     /** {@inheritDoc} */
145     public void init(FilterConfig config) throws ServletException {
146         filterConfig = config;
147         servletContext = filterConfig.getServletContext();
148 
149         containerKey = filterConfig
150                 .getInitParameter(CONTAINER_KEY_INIT_PARAMETER);
151 
152         String temp = config.getInitParameter("attribute-name");
153         if (temp != null) {
154             definitionAttributeName = temp;
155         }
156 
157         temp = config.getInitParameter("definition");
158         if (temp != null) {
159             definitionName = temp;
160         }
161 
162         temp = config.getInitParameter("prevent-token");
163         preventDecorationToken = "org.apache.tiles.decoration.PREVENT:"
164                 + (temp == null ? definitionName : temp);
165 
166         alternateDefinitions = parseAlternateDefinitions();
167 
168         temp = config.getInitParameter("mutator");
169         if (temp != null) {
170             try {
171                 mutator = (AttributeContextMutator) ClassUtil.instantiate(temp);
172             } catch (CannotInstantiateObjectException e) {
173                 throw new ServletException("Unable to instantiate specified context mutator.", e);
174             }
175         } else {
176             mutator = new DefaultMutator();
177         }
178     }
179 
180     /**
181      * Creates the alternate definitions map, to map a mask of definition names
182      * to a configured definition.
183      *
184      * @return The alternate definitions map.
185      */
186     @SuppressWarnings("unchecked")
187     protected Map<String, String> parseAlternateDefinitions() {
188         Map<String, String> map = new HashMap<String, String>();
189         Enumeration<String> e = filterConfig.getInitParameterNames();
190         while (e.hasMoreElements()) {
191             String parm = e.nextElement();
192             if (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         }
201         return map;
202     }
203 
204     /** {@inheritDoc} */
205     public void destroy() {
206         filterConfig = null;
207     }
208 
209 
210     /**
211      * {@inheritDoc}
212      */
213     public void doFilter(javax.servlet.ServletRequest req, ServletResponse res, FilterChain filterChain)
214             throws 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 infinately
217         // loop.
218         if (!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);
223             TilesContainer container = TilesAccess.getContainer(applicationContext,
224                     containerKey);
225             mutator.mutate(container.getAttributeContext(request), req);
226             if (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     }
234 
235     /**
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      */
241     private String getDefinitionForRequest(javax.servlet.ServletRequest request) {
242         if (alternateDefinitions.size() < 1) {
243             return definitionName;
244         }
245         String base = getRequestBase(request);
246         for (Map.Entry<String, String> pair : alternateDefinitions.entrySet()) {
247             if (base.startsWith(pair.getKey())) {
248                 return pair.getValue();
249             }
250         }
251         return definitionName;
252     }
253 
254     /**
255      * Returns the request base, i.e. the the URL to calculate all the relative
256      * paths.
257      *
258      * @param request The request object to use.
259      * @return The request base.
260      */
261     private String getRequestBase(javax.servlet.ServletRequest request) {
262         // Included Path
263         String include = (String) request.getAttribute("javax.servlet.include.servlet_path");
264         if (include != null) {
265             return include;
266         }
267 
268         // As opposed to includes, if a forward occurs, it will update the servletPath property
269         // and include the original as the request attribute.
270         return ((HttpServletRequest) request).getServletPath();
271     }
272 
273     /**
274      * The default attribute context mutator to use.
275      */
276     class DefaultMutator implements AttributeContextMutator {
277 
278         /** {@inheritDoc} */
279         public void mutate(AttributeContext ctx, javax.servlet.ServletRequest req) {
280             Attribute attr = new Attribute();
281             attr.setRenderer("template");
282             attr.setValue(getRequestBase(req));
283             ctx.putAttribute(definitionAttributeName, attr);
284         }
285     }
286 
287     /**
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      */
293     private boolean isPreventTokenPresent(javax.servlet.ServletRequest request) {
294         return preventDecorationToken != null && request.getAttribute(preventDecorationToken) != null;
295     }
296 }