Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ShaleApplicationFilter |
|
| 7.5;7.5 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.shale.application.faces; | |
19 | ||
20 | import java.io.IOException; | |
21 | import java.lang.reflect.Method; | |
22 | import java.net.URL; | |
23 | ||
24 | import javax.servlet.Filter; | |
25 | import javax.servlet.FilterChain; | |
26 | import javax.servlet.FilterConfig; | |
27 | import javax.servlet.ServletContext; | |
28 | import javax.servlet.ServletException; | |
29 | import javax.servlet.ServletRequest; | |
30 | import javax.servlet.ServletResponse; | |
31 | import javax.servlet.http.HttpServletRequest; | |
32 | import javax.servlet.http.HttpServletResponse; | |
33 | ||
34 | import org.apache.commons.chain.Catalog; | |
35 | import org.apache.commons.chain.CatalogFactory; | |
36 | import org.apache.commons.chain.Command; | |
37 | import org.apache.commons.chain.config.ConfigParser; | |
38 | import org.apache.commons.chain.impl.CatalogBase; | |
39 | import org.apache.commons.chain.web.WebContext; | |
40 | import org.apache.commons.chain.web.servlet.ServletWebContext; | |
41 | import org.apache.commons.logging.Log; | |
42 | import org.apache.commons.logging.LogFactory; | |
43 | import org.apache.shale.util.Messages; | |
44 | ||
45 | ||
46 | /** | |
47 | * <p>{@link ShaleApplicationFilter} is a <code>Filter</code> implementation | |
48 | * that invokes the required <em>Application Controller</em> functionality on | |
49 | * every request. | |
50 | * In addition, it performs overall application startup and shutdown | |
51 | * operations on behalf of the framework.</p> | |
52 | * | |
53 | * <p>The detailed processing to be performed for each request is configured | |
54 | * by a <code>Command</code> or <code>Chain</code> defined using the "Chain of | |
55 | * Resposibility" design pattern, as implemented by the Commons Chain package. | |
56 | * There must exist a <code>Catalog</code> named <code>shale</code>, which | |
57 | * contains a <code>Command</code> named <code>standard</code>, that defines | |
58 | * the processing to be performed.</p> | |
59 | * | |
60 | * <p>At any point, one of the <code>Command</code>s being executed may choose | |
61 | * to complete the response itself (such as to perform an HTTP redirect), | |
62 | * instead of allowing processing to continue. To indicate this choice, the | |
63 | * <code>Command</code> should follow the standard Commons Chain convention of | |
64 | * returning <code>true</code>. If you want processing to continue, return | |
65 | * <code>false</code> instead.</p> | |
66 | * | |
67 | * <p>The default implementation of the standard command processing chain | |
68 | * performs the following tasks:</p> | |
69 | * <ul> | |
70 | * <li>Invoke a command named <code>preprocess</code> (in the <code>shale</code> | |
71 | * catalog), if it exists. This is where you should insert commands to be | |
72 | * executed <strong>before</strong> {@link ShaleApplicationFilter} passes the | |
73 | * request on to the next filter or servlet.</li> | |
74 | * <li>Execute the remainder of the filter chain for this request.</li> | |
75 | * <li>Invokes a command named <code>postprocess</code> (in the <code>shale</code> | |
76 | * catalog), if it exists. This is where you should insert commands to be | |
77 | * executed <strong>after</strong> control returns from the invoked filter or | |
78 | * servlet. Note that it is no longer possible, at this point, to replace | |
79 | * the response content produced by the filter or servlet -- that should | |
80 | * be done in a preprocess step.</li> | |
81 | * </ul> | |
82 | * | |
83 | * <p><strong>NOTE</strong> - Configuration of the <code>shale</code> catalog, | |
84 | * and the commands it contains, may be performed in any manner you desire. | |
85 | * One convenient mechanism is to use the <code>ChainListener</code> class | |
86 | * that is included in the Commons Chain package. If you do not reconfigure it | |
87 | * differently, the <code>standard</code> command (in the <code>shale</code> | |
88 | * catalog) will be configured according to the embedded resource | |
89 | * <code>org/apache/shale/faces/shale-config.xml</code> in the JAR file | |
90 | * containing the core Shale runtime environment, which executes the default | |
91 | * request processing described above.</p> | |
92 | * | |
93 | * $Id: ShaleApplicationFilter.java 464373 2006-10-16 04:21:54Z rahul $ | |
94 | */ | |
95 | ||
96 | 0 | public class ShaleApplicationFilter implements Filter { |
97 | ||
98 | ||
99 | // -------------------------------------------------------- Static Variables | |
100 | ||
101 | ||
102 | /** | |
103 | * <p>The name of the Commons Chain <code>Catalog</code> to use.</p> | |
104 | */ | |
105 | public static final String CATALOG_NAME = "shale"; | |
106 | ||
107 | ||
108 | /** | |
109 | * <p>The name of the <code>Command</code> to execute during | |
110 | * application shutdown.</p> | |
111 | */ | |
112 | public static final String COMMAND_DESTROY = "destroy"; | |
113 | ||
114 | ||
115 | /** | |
116 | * <p>The name of the <code>Command</code> to execute during | |
117 | * application startup.</p> | |
118 | */ | |
119 | public static final String COMMAND_INIT = "init"; | |
120 | ||
121 | ||
122 | /** | |
123 | * <p>The name of the <code>Command</code> to execute before | |
124 | * the application logic itself is invoked.</p> | |
125 | */ | |
126 | public static final String COMMAND_PREPROCESS = "preprocess"; | |
127 | ||
128 | ||
129 | /** | |
130 | * <p>The name of the <code>Command</code> to execute after | |
131 | * the application logic itself is invoked.</p> | |
132 | */ | |
133 | public static final String COMMAND_POSTPROCESS = "postprocess"; | |
134 | ||
135 | ||
136 | /** | |
137 | * <p>The request scope attribute key under which the <code>Context</code> | |
138 | * object used for this chain of command request to be stored, in addition | |
139 | * to it being passed in to the command chains.</p> | |
140 | */ | |
141 | public static final String CONTEXT_ATTR = | |
142 | "org.apache.shale.CONTEXT_ATTR"; | |
143 | ||
144 | ||
145 | /** | |
146 | * <p>The name of the internal resource containing our default | |
147 | * configuration of the default command.</p> | |
148 | */ | |
149 | public static final String RESOURCE_NAME = | |
150 | "org/apache/shale/application/faces/shale-config.xml"; | |
151 | ||
152 | ||
153 | // ------------------------------------------------------ Instance Variables | |
154 | ||
155 | ||
156 | /** | |
157 | * <p>Chain of Responsibility <code>Catalog</code> we will be using.</p> | |
158 | */ | |
159 | 0 | private Catalog catalog = null; |
160 | ||
161 | ||
162 | /** | |
163 | * <p>The <code>ServletContext</code> instance for our web application.</p> | |
164 | */ | |
165 | 0 | private ServletContext context = null; |
166 | ||
167 | ||
168 | /** | |
169 | * <p>The <code>Log</code> instance for this class.</p> | |
170 | */ | |
171 | 0 | private transient Log log = null; |
172 | ||
173 | ||
174 | /** | |
175 | * <p>Message resources for this class.</p> | |
176 | */ | |
177 | 0 | private static Messages messages = |
178 | new Messages("org.apache.shale.resources.Bundle", | |
179 | 0 | ShaleApplicationFilter.class.getClassLoader()); |
180 | ||
181 | ||
182 | // ---------------------------------------------------------- Filter Methods | |
183 | ||
184 | ||
185 | /** | |
186 | * <p>Perform application shutdown finalization as necessary.</p> | |
187 | */ | |
188 | public void destroy() { | |
189 | ||
190 | 0 | if (log().isInfoEnabled()) { |
191 | 0 | log().info(messages.getMessage("filter.finalizing")); |
192 | } | |
193 | ||
194 | // Execute the "destroy" command in the "shale" catalog (if any) | |
195 | 0 | Command command = catalog.getCommand(COMMAND_DESTROY); |
196 | 0 | if (command != null) { |
197 | 0 | WebContext webContext = new ServletWebContext(context, null, null); |
198 | try { | |
199 | 0 | command.execute(webContext); |
200 | 0 | } catch (Exception e) { |
201 | 0 | if (log().isErrorEnabled()) { |
202 | 0 | log().error(messages.getMessage("filter.destroyException"), e); |
203 | } | |
204 | 0 | } |
205 | } | |
206 | ||
207 | // Clean up JavaServer Faces integration linkages | |
208 | 0 | context = null; |
209 | 0 | catalog = null; |
210 | ||
211 | // Clean up subordinate libraries as needed | |
212 | 0 | CatalogFactory.clear(); |
213 | 0 | cleanup(); |
214 | 0 | LogFactory.release(Thread.currentThread().getContextClassLoader()); |
215 | ||
216 | 0 | } |
217 | ||
218 | ||
219 | /** | |
220 | * <p>Perform per-request application controler functionality.</p> | |
221 | * | |
222 | * @param request The request we are processing | |
223 | * @param response The response we are creating | |
224 | * @param chain The filter chain for this request | |
225 | * | |
226 | * @exception IOException if an input/output error occurs | |
227 | * @exception ServletException if a servlet exception is thrown | |
228 | */ | |
229 | public void doFilter(ServletRequest request, ServletResponse response, | |
230 | FilterChain chain) throws IOException, ServletException { | |
231 | ||
232 | // Define local variables we will need | |
233 | 0 | Command command = null; |
234 | 0 | boolean result = false; |
235 | ||
236 | // Construct and store a new Context for this request | |
237 | 0 | ShaleWebContext context = |
238 | new ShaleWebContext(this.context, | |
239 | (HttpServletRequest) request, | |
240 | (HttpServletResponse) response); | |
241 | 0 | request.setAttribute(CONTEXT_ATTR, context); |
242 | ||
243 | // Invoke the "preprocess" command (if any is defined). If this | |
244 | // command returns true, the response has been completed already | |
245 | // so we do NOT invoke the actual application. | |
246 | 0 | command = catalog.getCommand(COMMAND_PREPROCESS); |
247 | 0 | if (command != null) { |
248 | try { | |
249 | 0 | result = command.execute(context); |
250 | 0 | } catch (IOException e) { |
251 | 0 | throw e; |
252 | 0 | } catch (ServletException e) { |
253 | 0 | throw e; |
254 | 0 | } catch (Exception e) { |
255 | 0 | throw new ServletException(e); |
256 | 0 | } |
257 | 0 | if (result) { |
258 | // Clean up the stored request attribute | |
259 | 0 | request.removeAttribute(CONTEXT_ATTR); |
260 | // Bypass calling the remainder of the application | |
261 | 0 | return; |
262 | } | |
263 | } | |
264 | ||
265 | // Invoke the remainder of the processing for this request | |
266 | // (which will typically be the JSF controller servlet) | |
267 | 0 | chain.doFilter(request, response); |
268 | ||
269 | // Invoke the "postprocess" command (if any is defined). | |
270 | 0 | command = catalog.getCommand(COMMAND_POSTPROCESS); |
271 | 0 | if (command != null) { |
272 | try { | |
273 | 0 | command.execute(context); |
274 | 0 | } catch (IOException e) { |
275 | 0 | throw e; |
276 | 0 | } catch (ServletException e) { |
277 | 0 | throw e; |
278 | 0 | } catch (Exception e) { |
279 | 0 | throw new ServletException(e); |
280 | 0 | } |
281 | } | |
282 | ||
283 | // Clean up the stored request attribute | |
284 | 0 | request.removeAttribute(CONTEXT_ATTR); |
285 | ||
286 | 0 | } |
287 | ||
288 | ||
289 | /** | |
290 | * <p>Perform application startup intiialization as necessary.</p> | |
291 | * | |
292 | * @param config <code>FilterConfig</code> for this filter | |
293 | * | |
294 | * @exception ServletException if a servlet exception is thrown | |
295 | */ | |
296 | public void init(FilterConfig config) throws ServletException { | |
297 | ||
298 | 0 | if (log().isInfoEnabled()) { |
299 | 0 | log().info(messages.getMessage("filter.initializing")); |
300 | } | |
301 | ||
302 | 0 | context = config.getServletContext(); |
303 | ||
304 | // Look up the "shale" catalog and ensure "standard" is defined | |
305 | try { | |
306 | 0 | catalog = getCatalog(); |
307 | 0 | } catch (ServletException e) { |
308 | 0 | throw e; |
309 | 0 | } catch (Exception e) { |
310 | 0 | throw new ServletException(e); |
311 | 0 | } |
312 | ||
313 | // Execute the "init" command in the "shale" catalog (if any) | |
314 | 0 | Command command = catalog.getCommand(COMMAND_INIT); |
315 | 0 | if (command != null) { |
316 | 0 | WebContext webContext = new ServletWebContext(context, null, null); |
317 | try { | |
318 | 0 | command.execute(webContext); |
319 | 0 | } catch (Exception e) { |
320 | 0 | if (log().isErrorEnabled()) { |
321 | 0 | log().error(messages.getMessage("filter.initException"), e); |
322 | } | |
323 | 0 | throw new ServletException(e); |
324 | 0 | } |
325 | } | |
326 | ||
327 | 0 | } |
328 | ||
329 | ||
330 | // --------------------------------------------------------- Private Methods | |
331 | ||
332 | ||
333 | /** | |
334 | * <p>Clean up the Commons BeanUtils library if it has been loaded.</p> | |
335 | */ | |
336 | private void cleanup() { | |
337 | ||
338 | try { | |
339 | 0 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
340 | 0 | if (loader == null) { |
341 | 0 | loader = ShaleApplicationFilter.class.getClassLoader(); |
342 | } | |
343 | 0 | Class clazz = loader.loadClass("org.apache.commons.beanutils.PropertyUtils"); |
344 | 0 | Method method = clazz.getMethod("clearDescriptors", (Class[]) null); |
345 | 0 | method.invoke(null, (Object[]) null); |
346 | 0 | } catch (Exception e) { |
347 | ; // Swallow and ignore any exceptions | |
348 | 0 | } |
349 | ||
350 | 0 | } |
351 | ||
352 | ||
353 | ||
354 | /** | |
355 | * <p>Return the "shale" catalog with a "standard" command, configuring the | |
356 | * default version of this command if necessary.</p> | |
357 | * | |
358 | * @exception Exception if a resource parsing exception occurs | |
359 | */ | |
360 | private Catalog getCatalog() throws Exception { | |
361 | ||
362 | // Look up the "shale" catalog, returning any existing instance | |
363 | // if it is fully configured | |
364 | 0 | Catalog catalog = CatalogFactory.getInstance().getCatalog(CATALOG_NAME); |
365 | 0 | if ((catalog != null) && |
366 | (catalog.getCommand(COMMAND_INIT) != null) && | |
367 | (catalog.getCommand(COMMAND_DESTROY) != null)) { | |
368 | 0 | return catalog; |
369 | } | |
370 | ||
371 | // Create a new catalog (if necessary) | |
372 | 0 | if (catalog == null) { |
373 | 0 | if (log().isDebugEnabled()) { |
374 | 0 | log().debug(messages.getMessage("filter.creatingCatalog", |
375 | new Object[] { CATALOG_NAME })); | |
376 | } | |
377 | 0 | catalog = new CatalogBase(); |
378 | 0 | CatalogFactory.getInstance().addCatalog(CATALOG_NAME, catalog); |
379 | } | |
380 | ||
381 | // Configure this catalog based on our default resource | |
382 | 0 | if (log().isDebugEnabled()) { |
383 | 0 | log().debug(messages.getMessage("filter.parsingResource", |
384 | new Object[] { RESOURCE_NAME })); | |
385 | } | |
386 | 0 | ConfigParser parser = new ConfigParser(); |
387 | 0 | URL url = this.getClass().getClassLoader().getResource(RESOURCE_NAME); |
388 | 0 | if (url == null) { |
389 | 0 | throw new IllegalArgumentException(RESOURCE_NAME); |
390 | } | |
391 | 0 | parser.parse(url); |
392 | ||
393 | 0 | return catalog; |
394 | ||
395 | } | |
396 | ||
397 | ||
398 | /** | |
399 | * <p>Return the <code>Log</code> instance to use, creating one if needed.</p> | |
400 | */ | |
401 | private Log log() { | |
402 | ||
403 | 0 | if (this.log == null) { |
404 | 0 | log = LogFactory.getLog(ShaleApplicationFilter.class); |
405 | } | |
406 | 0 | return log; |
407 | ||
408 | } | |
409 | ||
410 | ||
411 | } |