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