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.InvalidCancelException;
46 import org.apache.struts.config.FormBeanConfig;
47 import org.apache.struts.config.ForwardConfig;
48 import org.apache.struts.faces.Constants;
49 import org.apache.struts.faces.component.FormComponent;
50 import org.apache.struts.tiles.TilesRequestProcessor;
51
52
53 /**
54 * <p>Concrete implementation of <code>RequestProcessor</code> that
55 * implements the standard Struts request processing lifecycle on a
56 * request that was received as an <code>ActionEvent</code> by our
57 * associated <code>ActionListener</code>. It replaces the request processor
58 * instance normally configured by Struts+Tiles, so it must support non-Faces
59 * requests as well.</p>
60 *
61 * @version $Rev: 473326 $ $Date: 2006-11-10 06:58:06 -0600 (Fri, 10 Nov 2006) $
62 */
63
64 public class FacesTilesRequestProcessor extends TilesRequestProcessor {
65
66
67
68
69
70 /**
71 * <p>The log instance for this class.</p>
72 */
73 protected static Log log =
74 LogFactory.getLog(FacesTilesRequestProcessor.class);
75
76 /**
77 * <p>The lifecycle id.</p>
78 */
79 public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID";
80
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 void internalModuleRelativeForward
176 (String uri, HttpServletRequest request, HttpServletResponse response)
177 throws IOException, ServletException {
178
179 if (log.isTraceEnabled()) {
180 log.trace("Performing internal module relative forward to '" +
181 uri + "'");
182 }
183 super.internalModuleRelativeForward(uri, request, response);
184
185 }
186
187
188
189 protected Action processActionCreate(HttpServletRequest request,
190 HttpServletResponse response,
191 ActionMapping mapping)
192 throws IOException {
193
194 if (log.isTraceEnabled()) {
195 log.trace("Performing standard action create");
196 }
197 Action result = super.processActionCreate(request, response, mapping);
198 if (log.isDebugEnabled()) {
199 log.debug("Standard action create returned " +
200 result.getClass().getName() + " instance");
201 }
202 return (result);
203
204 }
205
206
207
208 protected ActionForm processActionForm(HttpServletRequest request,
209 HttpServletResponse response,
210 ActionMapping mapping) {
211 if (log.isTraceEnabled()) {
212 log.trace("Performing standard action form processing");
213 String attribute = mapping.getAttribute();
214 if (attribute != null) {
215 String name = mapping.getName();
216 FormBeanConfig fbc = moduleConfig.findFormBeanConfig(name);
217 if (fbc != null) {
218 if ("request".equals(mapping.getScope())) {
219 log.trace(" Bean in request scope = " +
220 request.getAttribute(attribute));
221 } else {
222 log.trace(" Bean in session scope = " +
223 request.getSession().getAttribute(attribute));
224 }
225 } else {
226 log.trace(" No FormBeanConfig for '" + name + "'");
227 }
228 } else {
229 log.trace(" No form bean for this action");
230 }
231 }
232 ActionForm result =
233 super.processActionForm(request, response, mapping);
234 if (log.isDebugEnabled()) {
235 log.debug("Standard action form returned " +
236 result);
237 }
238 return (result);
239
240
241 }
242
243
244
245 protected ActionForward processActionPerform(HttpServletRequest request,
246 HttpServletResponse response,
247 Action action,
248 ActionForm form,
249 ActionMapping mapping)
250 throws IOException, ServletException {
251
252 if (log.isTraceEnabled()) {
253 log.trace("Performing standard action perform");
254 }
255 ActionForward result =
256 super.processActionPerform(request, response, action,
257 form, mapping);
258 if (log.isDebugEnabled()) {
259 log.debug("Standard action perform returned " +
260 (result == null ? "NULL" :
261 result.getPath()) + " forward path");
262 }
263 return (result);
264
265 }
266
267
268
269 protected boolean processForward(HttpServletRequest request,
270 HttpServletResponse response,
271 ActionMapping mapping)
272 throws IOException, ServletException {
273
274 if (log.isTraceEnabled()) {
275 log.trace("Performing standard forward handling");
276 }
277 boolean result = super.processForward
278 (request, response, mapping);
279 if (log.isDebugEnabled()) {
280 log.debug("Standard forward handling returned " + result);
281 }
282 return (result);
283
284 }
285
286
287
288 protected void processForwardConfig(HttpServletRequest request,
289 HttpServletResponse response,
290 ForwardConfig forward)
291 throws IOException, ServletException {
292
293 if (log.isTraceEnabled()) {
294 log.trace("Performing standard forward config handling");
295 }
296 super.processForwardConfig(request, response, forward);
297 if (log.isDebugEnabled()) {
298 log.debug("Standard forward config handling completed");
299 }
300
301 }
302
303
304
305 protected boolean processInclude(HttpServletRequest request,
306 HttpServletResponse response,
307 ActionMapping mapping)
308 throws IOException, ServletException {
309
310 if (log.isTraceEnabled()) {
311 log.trace("Performing standard include handling");
312 }
313 boolean result = super.processInclude
314 (request, response, mapping);
315 if (log.isDebugEnabled()) {
316 log.debug("Standard include handling returned " + result);
317 }
318 return (result);
319
320 }
321
322
323 /**
324 * <p>Identify and return the path component (from the request URI for a
325 * non-Faces request, or from the form event for a Faces request)
326 * that we will use to select an ActionMapping to dispatch with.
327 * If no such path can be identified, create an error response and return
328 * <code>null</code>.</p>
329 *
330 * @param request The servlet request we are processing
331 * @param response The servlet response we are creating
332 *
333 * @exception IOException if an input/output error occurs
334 */
335 protected String processPath(HttpServletRequest request,
336 HttpServletResponse response)
337 throws IOException {
338
339
340 ActionEvent event = (ActionEvent)
341 request.getAttribute(Constants.ACTION_EVENT_KEY);
342
343
344 if (event == null) {
345 if (log.isTraceEnabled()) {
346 log.trace("Performing standard processPath() processing");
347 }
348 return (super.processPath(request, response));
349 }
350
351
352 UIComponent component = event.getComponent();
353 if (log.isTraceEnabled()) {
354 log.trace("Locating form parent for command component " +
355 event.getComponent());
356 }
357 while (!(component instanceof FormComponent)) {
358 component = component.getParent();
359 if (component == null) {
360 log.warn("Command component was not nested in a Struts form!");
361 return (null);
362 }
363 }
364 if (log.isTraceEnabled()) {
365 log.trace("Returning selected path of " +
366 ((FormComponent) component).getAction());
367 }
368 return (((FormComponent) component).getAction());
369
370 }
371
372
373 /**
374 * <p>Populate the properties of the specified <code>ActionForm</code>
375 * instance from the request parameters included with this request,
376 * <strong>IF</strong> this is a non-Faces request. For a Faces request,
377 * this will have already been done by the <em>Update Model Values</em>
378 * phase of the request processing lifecycle, so all we have to do is
379 * recognize whether the request was cancelled or not.</p>
380 *
381 * @param request The servlet request we are processing
382 * @param response The servlet response we are creating
383 * @param form The ActionForm instance we are populating
384 * @param mapping The ActionMapping we are using
385 *
386 * @exception ServletException if thrown by RequestUtils.populate()
387 */
388 protected void processPopulate(HttpServletRequest request,
389 HttpServletResponse response,
390 ActionForm form,
391 ActionMapping mapping)
392 throws ServletException {
393
394
395 ActionEvent event = (ActionEvent)
396 request.getAttribute(Constants.ACTION_EVENT_KEY);
397
398
399 if (event == null) {
400 if (log.isTraceEnabled()) {
401 log.trace("Performing standard processPopulate() processing");
402 }
403 super.processPopulate(request, response, form, mapping);
404 return;
405 }
406
407
408
409 if (log.isTraceEnabled()) {
410 log.trace("Faces request, so no processPopulate() processing");
411 }
412 UIComponent source = event.getComponent();
413 if (source instanceof UICommand) {
414 if ("cancel".equals(((UICommand) source).getId())) {
415 if (log.isTraceEnabled()) {
416 log.trace("Faces request with cancel button pressed");
417 }
418 request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
419 }
420 }
421
422 }
423
424
425
426 protected boolean processValidate(HttpServletRequest request,
427 HttpServletResponse response,
428 ActionForm form,
429 ActionMapping mapping)
430 throws IOException, ServletException, InvalidCancelException {
431
432 if (log.isTraceEnabled()) {
433 log.trace("Performing standard validation");
434 }
435 boolean result = super.processValidate
436 (request, response, form, mapping);
437 if (log.isDebugEnabled()) {
438 log.debug("Standard validation processing returned " + result);
439 }
440 return (result);
441
442 }
443
444
445
446
447
448 /**
449 * <p>Return the used Lifecycle ID (default or custom).</p>
450 */
451 private String getLifecycleId()
452 {
453 String lifecycleId = this.servlet.getServletContext().getInitParameter(LIFECYCLE_ID_ATTR);
454 return lifecycleId != null ? lifecycleId : LifecycleFactory.DEFAULT_LIFECYCLE;
455 }
456
457 /**
458 * <p>Return <code>true</code> if the specified context-relative URI
459 * specifies a request to be processed by the Struts controller servlet.</p>
460 *
461 * @param uri URI to be checked
462 */
463 private boolean isStrutsRequest(String uri) {
464
465 int question = uri.indexOf("?");
466 if (question >= 0) {
467 uri = uri.substring(0, question);
468 }
469 String mapping = (String)
470 servlet.getServletContext().getAttribute(Globals.SERVLET_KEY);
471 if (mapping == null) {
472 return (false);
473 } else if (mapping.startsWith("*.")) {
474 return (uri.endsWith(mapping.substring(1)));
475 } else if (mapping.endsWith("/*")) {
476 return (uri.startsWith(mapping.substring(0, mapping.length() - 2)));
477 } else {
478 return (false);
479 }
480
481 }
482
483
484 }