2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 public class ShaleApplicationFilter implements Filter {
97
98
99
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
154
155
156 /***
157 * <p>Chain of Responsibility <code>Catalog</code> we will be using.</p>
158 */
159 private Catalog catalog = null;
160
161
162 /***
163 * <p>The <code>ServletContext</code> instance for our web application.</p>
164 */
165 private ServletContext context = null;
166
167
168 /***
169 * <p>The <code>Log</code> instance for this class.</p>
170 */
171 private transient Log log = null;
172
173
174 /***
175 * <p>Message resources for this class.</p>
176 */
177 private static Messages messages =
178 new Messages("org.apache.shale.resources.Bundle",
179 ShaleApplicationFilter.class.getClassLoader());
180
181
182
183
184
185 /***
186 * <p>Perform application shutdown finalization as necessary.</p>
187 */
188 public void destroy() {
189
190 if (log().isInfoEnabled()) {
191 log().info(messages.getMessage("filter.finalizing"));
192 }
193
194
195 Command command = catalog.getCommand(COMMAND_DESTROY);
196 if (command != null) {
197 WebContext webContext = new ServletWebContext(context, null, null);
198 try {
199 command.execute(webContext);
200 } catch (Exception e) {
201 if (log().isErrorEnabled()) {
202 log().error(messages.getMessage("filter.destroyException"), e);
203 }
204 }
205 }
206
207
208 context = null;
209 catalog = null;
210
211
212 CatalogFactory.clear();
213 cleanup();
214 LogFactory.release(Thread.currentThread().getContextClassLoader());
215
216 }
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
233 Command command = null;
234 boolean result = false;
235
236
237 ShaleWebContext context =
238 new ShaleWebContext(this.context,
239 (HttpServletRequest) request,
240 (HttpServletResponse) response);
241 request.setAttribute(CONTEXT_ATTR, context);
242
243
244
245
246 command = catalog.getCommand(COMMAND_PREPROCESS);
247 if (command != null) {
248 try {
249 result = command.execute(context);
250 } catch (IOException e) {
251 throw e;
252 } catch (ServletException e) {
253 throw e;
254 } catch (Exception e) {
255 throw new ServletException(e);
256 }
257 if (result) {
258
259 request.removeAttribute(CONTEXT_ATTR);
260
261 return;
262 }
263 }
264
265
266
267 chain.doFilter(request, response);
268
269
270 command = catalog.getCommand(COMMAND_POSTPROCESS);
271 if (command != null) {
272 try {
273 command.execute(context);
274 } catch (IOException e) {
275 throw e;
276 } catch (ServletException e) {
277 throw e;
278 } catch (Exception e) {
279 throw new ServletException(e);
280 }
281 }
282
283
284 request.removeAttribute(CONTEXT_ATTR);
285
286 }
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 if (log().isInfoEnabled()) {
299 log().info(messages.getMessage("filter.initializing"));
300 }
301
302 context = config.getServletContext();
303
304
305 try {
306 catalog = getCatalog();
307 } catch (ServletException e) {
308 throw e;
309 } catch (Exception e) {
310 throw new ServletException(e);
311 }
312
313
314 Command command = catalog.getCommand(COMMAND_INIT);
315 if (command != null) {
316 WebContext webContext = new ServletWebContext(context, null, null);
317 try {
318 command.execute(webContext);
319 } catch (Exception e) {
320 if (log().isErrorEnabled()) {
321 log().error(messages.getMessage("filter.initException"), e);
322 }
323 throw new ServletException(e);
324 }
325 }
326
327 }
328
329
330
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 ClassLoader loader = Thread.currentThread().getContextClassLoader();
340 if (loader == null) {
341 loader = ShaleApplicationFilter.class.getClassLoader();
342 }
343 Class clazz = loader.loadClass("org.apache.commons.beanutils.PropertyUtils");
344 Method method = clazz.getMethod("clearDescriptors", (Class[]) null);
345 method.invoke(null, (Object[]) null);
346 } catch (Exception e) {
347 ;
348 }
349
350 }
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
363
364 Catalog catalog = CatalogFactory.getInstance().getCatalog(CATALOG_NAME);
365 if ((catalog != null) &&
366 (catalog.getCommand(COMMAND_INIT) != null) &&
367 (catalog.getCommand(COMMAND_DESTROY) != null)) {
368 return catalog;
369 }
370
371
372 if (catalog == null) {
373 if (log().isDebugEnabled()) {
374 log().debug(messages.getMessage("filter.creatingCatalog",
375 new Object[] { CATALOG_NAME }));
376 }
377 catalog = new CatalogBase();
378 CatalogFactory.getInstance().addCatalog(CATALOG_NAME, catalog);
379 }
380
381
382 if (log().isDebugEnabled()) {
383 log().debug(messages.getMessage("filter.parsingResource",
384 new Object[] { RESOURCE_NAME }));
385 }
386 ConfigParser parser = new ConfigParser();
387 URL url = this.getClass().getClassLoader().getResource(RESOURCE_NAME);
388 if (url == null) {
389 throw new IllegalArgumentException(RESOURCE_NAME);
390 }
391 parser.parse(url);
392
393 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 if (this.log == null) {
404 log = LogFactory.getLog(ShaleApplicationFilter.class);
405 }
406 return log;
407
408 }
409
410
411 }