1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.struts.faces.application;
18
19
20 import java.io.IOException;
21 import javax.faces.FactoryFinder;
22 import javax.faces.application.ViewHandler;
23 import javax.faces.component.UICommand;
24 import javax.faces.component.UIComponent;
25 import javax.faces.context.FacesContext;
26 import javax.faces.context.FacesContextFactory;
27 import javax.faces.event.ActionEvent;
28 import javax.faces.lifecycle.Lifecycle;
29 import javax.faces.lifecycle.LifecycleFactory;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.struts.Globals;
36 import org.apache.struts.action.Action;
37 import org.apache.struts.action.ActionForm;
38 import org.apache.struts.action.ActionForward;
39 import org.apache.struts.action.ActionMapping;
40 import org.apache.struts.action.InvalidCancelException;
41 import org.apache.struts.config.FormBeanConfig;
42 import org.apache.struts.config.ForwardConfig;
43 import org.apache.struts.faces.Constants;
44 import org.apache.struts.faces.component.FormComponent;
45 import org.apache.struts.tiles.TilesRequestProcessor;
46
47
48 /***
49 * <p>Concrete implementation of <code>RequestProcessor</code> that
50 * implements the standard Struts request processing lifecycle on a
51 * request that was received as an <code>ActionEvent</code> by our
52 * associated <code>ActionListener</code>. It replaces the request processor
53 * instance normally configured by Struts+Tiles, so it must support non-Faces
54 * requests as well.</p>
55 *
56 * @version $Rev: 421138 $ $Date: 2006-07-11 22:41:40 -0700 (Tue, 11 Jul 2006) $
57 */
58
59 public class FacesTilesRequestProcessor extends TilesRequestProcessor {
60
61
62
63
64
65 /***
66 * <p>The log instance for this class.</p>
67 */
68 protected static Log log =
69 LogFactory.getLog(FacesTilesRequestProcessor.class);
70
71
72
73
74
75
76 /***
77 * <p>Set up a Faces Request if we are not already processing one. Next,
78 * create a new view if the specified <code>uri</code> is different from
79 * the current view identifier. Finally, cause the new view to be
80 * rendered, and call <code>FacesContext.responseComplete()</code> to
81 * indicate that this has already been done.</p>
82 *
83 * @param uri Context-relative path to forward to
84 * @param request Current page request
85 * @param response Current page response
86 *
87 * @exception IOException if an input/output error occurs
88 * @exception ServletException if a servlet error occurs
89 */
90 protected void doForward(String uri,
91 HttpServletRequest request,
92 HttpServletResponse response)
93 throws IOException, ServletException {
94
95 if (log.isDebugEnabled()) {
96 log.debug("doForward(" + uri + ")");
97 }
98
99
100 request.removeAttribute(Constants.ACTION_EVENT_KEY);
101
102
103 if (isStrutsRequest(uri)) {
104 if (response.isCommitted()) {
105 if (log.isTraceEnabled()) {
106 log.trace(" super.doInclude(" + uri + ")");
107 }
108 super.doInclude(uri, request, response);
109 } else {
110 if (log.isTraceEnabled()) {
111 log.trace(" super.doForward(" + uri + ")");
112 }
113 super.doForward(uri, request, response);
114 }
115 return;
116 }
117
118
119 LifecycleFactory lf = (LifecycleFactory)
120 FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
121 Lifecycle lifecycle =
122 lf.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
123 boolean created = false;
124 FacesContext context = FacesContext.getCurrentInstance();
125 if (context == null) {
126 if (log.isTraceEnabled()) {
127 log.trace(" Creating new FacesContext for '" + uri + "'");
128 }
129 created = true;
130 FacesContextFactory fcf = (FacesContextFactory)
131 FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
132 context = fcf.getFacesContext(servlet.getServletContext(),
133 request, response, lifecycle);
134 }
135
136
137 ViewHandler vh = context.getApplication().getViewHandler();
138 if (log.isTraceEnabled()) {
139 log.trace(" Creating new view for '" + uri + "'");
140 }
141 context.setViewRoot(vh.createView(context, uri));
142
143
144 if (log.isTraceEnabled()) {
145 log.trace(" Rendering view for '" + uri + "'");
146 }
147 try {
148 lifecycle.render(context);
149 } finally {
150 if (created) {
151 if (log.isTraceEnabled()) {
152 log.trace(" Releasing context for '" + uri + "'");
153 }
154 context.release();
155 } else {
156 if (log.isTraceEnabled()) {
157 log.trace(" Rendering completed");
158 }
159 }
160 }
161
162 }
163
164
165
166 protected void internalModuleRelativeForward
167 (String uri, HttpServletRequest request, HttpServletResponse response)
168 throws IOException, ServletException {
169
170 if (log.isTraceEnabled()) {
171 log.trace("Performing internal module relative forward to '" +
172 uri + "'");
173 }
174 super.internalModuleRelativeForward(uri, request, response);
175
176 }
177
178
179
180 protected Action processActionCreate(HttpServletRequest request,
181 HttpServletResponse response,
182 ActionMapping mapping)
183 throws IOException {
184
185 if (log.isTraceEnabled()) {
186 log.trace("Performing standard action create");
187 }
188 Action result = super.processActionCreate(request, response, mapping);
189 if (log.isDebugEnabled()) {
190 log.debug("Standard action create returned " +
191 result.getClass().getName() + " instance");
192 }
193 return (result);
194
195 }
196
197
198
199 protected ActionForm processActionForm(HttpServletRequest request,
200 HttpServletResponse response,
201 ActionMapping mapping) {
202 if (log.isTraceEnabled()) {
203 log.trace("Performing standard action form processing");
204 String attribute = mapping.getAttribute();
205 if (attribute != null) {
206 String name = mapping.getName();
207 FormBeanConfig fbc = moduleConfig.findFormBeanConfig(name);
208 if (fbc != null) {
209 if ("request".equals(mapping.getScope())) {
210 log.trace(" Bean in request scope = " +
211 request.getAttribute(attribute));
212 } else {
213 log.trace(" Bean in session scope = " +
214 request.getSession().getAttribute(attribute));
215 }
216 } else {
217 log.trace(" No FormBeanConfig for '" + name + "'");
218 }
219 } else {
220 log.trace(" No form bean for this action");
221 }
222 }
223 ActionForm result =
224 super.processActionForm(request, response, mapping);
225 if (log.isDebugEnabled()) {
226 log.debug("Standard action form returned " +
227 result);
228 }
229 return (result);
230
231
232 }
233
234
235
236 protected ActionForward processActionPerform(HttpServletRequest request,
237 HttpServletResponse response,
238 Action action,
239 ActionForm form,
240 ActionMapping mapping)
241 throws IOException, ServletException {
242
243 if (log.isTraceEnabled()) {
244 log.trace("Performing standard action perform");
245 }
246 ActionForward result =
247 super.processActionPerform(request, response, action,
248 form, mapping);
249 if (log.isDebugEnabled()) {
250 log.debug("Standard action perform returned " +
251 (result == null ? "NULL" :
252 result.getPath()) + " forward path");
253 }
254 return (result);
255
256 }
257
258
259
260 protected boolean processForward(HttpServletRequest request,
261 HttpServletResponse response,
262 ActionMapping mapping)
263 throws IOException, ServletException {
264
265 if (log.isTraceEnabled()) {
266 log.trace("Performing standard forward handling");
267 }
268 boolean result = super.processForward
269 (request, response, mapping);
270 if (log.isDebugEnabled()) {
271 log.debug("Standard forward handling returned " + result);
272 }
273 return (result);
274
275 }
276
277
278
279 protected void processForwardConfig(HttpServletRequest request,
280 HttpServletResponse response,
281 ForwardConfig forward)
282 throws IOException, ServletException {
283
284 if (log.isTraceEnabled()) {
285 log.trace("Performing standard forward config handling");
286 }
287 super.processForwardConfig(request, response, forward);
288 if (log.isDebugEnabled()) {
289 log.debug("Standard forward config handling completed");
290 }
291
292 }
293
294
295
296 protected boolean processInclude(HttpServletRequest request,
297 HttpServletResponse response,
298 ActionMapping mapping)
299 throws IOException, ServletException {
300
301 if (log.isTraceEnabled()) {
302 log.trace("Performing standard include handling");
303 }
304 boolean result = super.processInclude
305 (request, response, mapping);
306 if (log.isDebugEnabled()) {
307 log.debug("Standard include handling returned " + result);
308 }
309 return (result);
310
311 }
312
313
314 /***
315 * <p>Identify and return the path component (from the request URI for a
316 * non-Faces request, or from the form event for a Faces request)
317 * that we will use to select an ActionMapping to dispatch with.
318 * If no such path can be identified, create an error response and return
319 * <code>null</code>.</p>
320 *
321 * @param request The servlet request we are processing
322 * @param response The servlet response we are creating
323 *
324 * @exception IOException if an input/output error occurs
325 */
326 protected String processPath(HttpServletRequest request,
327 HttpServletResponse response)
328 throws IOException {
329
330
331 ActionEvent event = (ActionEvent)
332 request.getAttribute(Constants.ACTION_EVENT_KEY);
333
334
335 if (event == null) {
336 if (log.isTraceEnabled()) {
337 log.trace("Performing standard processPath() processing");
338 }
339 return (super.processPath(request, response));
340 }
341
342
343 UIComponent component = event.getComponent();
344 if (log.isTraceEnabled()) {
345 log.trace("Locating form parent for command component " +
346 event.getComponent());
347 }
348 while (!(component instanceof FormComponent)) {
349 component = component.getParent();
350 if (component == null) {
351 log.warn("Command component was not nested in a Struts form!");
352 return (null);
353 }
354 }
355 if (log.isTraceEnabled()) {
356 log.trace("Returning selected path of " +
357 ((FormComponent) component).getAction());
358 }
359 return (((FormComponent) component).getAction());
360
361 }
362
363
364 /***
365 * <p>Populate the properties of the specified <code>ActionForm</code>
366 * instance from the request parameters included with this request,
367 * <strong>IF</strong> this is a non-Faces request. For a Faces request,
368 * this will have already been done by the <em>Update Model Values</em>
369 * phase of the request processing lifecycle, so all we have to do is
370 * recognize whether the request was cancelled or not.</p>
371 *
372 * @param request The servlet request we are processing
373 * @param response The servlet response we are creating
374 * @param form The ActionForm instance we are populating
375 * @param mapping The ActionMapping we are using
376 *
377 * @exception ServletException if thrown by RequestUtils.populate()
378 */
379 protected void processPopulate(HttpServletRequest request,
380 HttpServletResponse response,
381 ActionForm form,
382 ActionMapping mapping)
383 throws ServletException {
384
385
386 ActionEvent event = (ActionEvent)
387 request.getAttribute(Constants.ACTION_EVENT_KEY);
388
389
390 if (event == null) {
391 if (log.isTraceEnabled()) {
392 log.trace("Performing standard processPopulate() processing");
393 }
394 super.processPopulate(request, response, form, mapping);
395 return;
396 }
397
398
399
400 if (log.isTraceEnabled()) {
401 log.trace("Faces request, so no processPopulate() processing");
402 }
403 UIComponent source = event.getComponent();
404 if (source instanceof UICommand) {
405 if ("cancel".equals(((UICommand) source).getId())) {
406 if (log.isTraceEnabled()) {
407 log.trace("Faces request with cancel button pressed");
408 }
409 request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
410 }
411 }
412
413 }
414
415
416
417 protected boolean processValidate(HttpServletRequest request,
418 HttpServletResponse response,
419 ActionForm form,
420 ActionMapping mapping)
421 throws IOException, ServletException, InvalidCancelException {
422
423 if (log.isTraceEnabled()) {
424 log.trace("Performing standard validation");
425 }
426 boolean result = super.processValidate
427 (request, response, form, mapping);
428 if (log.isDebugEnabled()) {
429 log.debug("Standard validation processing returned " + result);
430 }
431 return (result);
432
433 }
434
435
436
437
438
439 /***
440 * <p>Return <code>true</code> if the specified context-relative URI
441 * specifies a request to be processed by the Struts controller servlet.</p>
442 *
443 * @param uri URI to be checked
444 */
445 private boolean isStrutsRequest(String uri) {
446
447 int question = uri.indexOf("?");
448 if (question >= 0) {
449 uri = uri.substring(0, question);
450 }
451 String mapping = (String)
452 servlet.getServletContext().getAttribute(Globals.SERVLET_KEY);
453 if (mapping == null) {
454 return (false);
455 } else if (mapping.startsWith("*.")) {
456 return (uri.endsWith(mapping.substring(1)));
457 } else if (mapping.endsWith("/*")) {
458 return (uri.startsWith(mapping.substring(0, mapping.length() - 2)));
459 } else {
460 return (false);
461 }
462
463 }
464
465
466 }