View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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.RequestProcessor;
41  import org.apache.struts.action.InvalidCancelException;
42  import org.apache.struts.config.FormBeanConfig;
43  import org.apache.struts.config.ForwardConfig;
44  import org.apache.struts.faces.Constants;
45  import org.apache.struts.faces.component.FormComponent;
46  
47  
48  
49  /***
50   * <p>Concrete implementation of <code>RequestProcessor</code> that
51   * implements the standard Struts request processing lifecycle on a
52   * request that was received as an <code>ActionEvent</code> by our
53   * associated <code>ActionListener</code>.  It replaces the request processor
54   * instance normally configured by Struts, so it must support non-Faces
55   * requests as well.</p>
56   *
57   * @version $Rev: 421138 $ $Date: 2006-07-11 22:41:40 -0700 (Tue, 11 Jul 2006) $
58   */
59  
60  public class FacesRequestProcessor extends RequestProcessor {
61  
62  
63      // ------------------------------------------------------ Instance Variables
64  
65  
66      /***
67       * <p>The log instance for this class.</p>
68       */
69      protected static Log log = LogFactory.getLog(FacesRequestProcessor.class);
70  
71  
72  
73      // ------------------------------------------------------- Protected Methods
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          // Remove the current ActionEvent (if any)
100         request.removeAttribute(Constants.ACTION_EVENT_KEY);
101 
102         // Process a Struts controller request normally
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         // Create a FacesContext for this request if necessary
119         LifecycleFactory lf = (LifecycleFactory)
120             FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
121         Lifecycle lifecycle = // FIXME - alternative lifecycle ids
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         // Create a new view root
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         // Cause the view to be rendered
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     // Override default processing to provide logging
166     protected Action processActionCreate(HttpServletRequest request,
167                                          HttpServletResponse response,
168                                          ActionMapping mapping)
169         throws IOException {
170 
171         if (log.isTraceEnabled()) {
172             log.trace("Performing standard action create");
173         }
174         Action result = super.processActionCreate(request, response, mapping);
175         if (log.isDebugEnabled()) {
176             log.debug("Standard action create returned " +
177                       result.getClass().getName() + " instance");
178         }
179         return (result);
180 
181     }
182 
183 
184     // Override default processing to provide logging
185     protected ActionForm processActionForm(HttpServletRequest request,
186                                            HttpServletResponse response,
187                                            ActionMapping mapping) {
188         if (log.isTraceEnabled()) {
189             log.trace("Performing standard action form processing");
190             String attribute = mapping.getAttribute();
191             if (attribute != null) {
192                 String name = mapping.getName();
193                 FormBeanConfig fbc = moduleConfig.findFormBeanConfig(name);
194                 if (fbc != null) {
195                     if ("request".equals(mapping.getScope())) {
196                         log.trace("  Bean in request scope = " +
197                                   request.getAttribute(attribute));
198                     } else {
199                         log.trace("  Bean in session scope = " +
200                                   request.getSession().getAttribute(attribute));
201                     }
202                 } else {
203                     log.trace("  No FormBeanConfig for '" + name + "'");
204                 }
205             } else {
206                 log.trace("  No form bean for this action");
207             }
208         }
209         ActionForm result =
210             super.processActionForm(request, response, mapping);
211         if (log.isDebugEnabled()) {
212             log.debug("Standard action form returned " +
213                       result);
214         }
215         return (result);
216 
217 
218     }
219 
220 
221     // Override default processing to provide logging
222     protected ActionForward processActionPerform(HttpServletRequest request,
223                                                  HttpServletResponse response,
224                                                  Action action,
225                                                  ActionForm form,
226                                                  ActionMapping mapping)
227         throws IOException, ServletException {
228 
229         if (log.isTraceEnabled()) {
230             log.trace("Performing standard action perform");
231         }
232         ActionForward result =
233             super.processActionPerform(request, response, action,
234                                        form, mapping);
235         if (log.isDebugEnabled()) {
236             log.debug("Standard action perform returned " +
237                       (result == null ? "NULL" :
238                       result.getPath()) + " forward path");
239         }
240         return (result);
241 
242     }
243 
244 
245     // Override default processing to provide logging
246     protected boolean processForward(HttpServletRequest request,
247                                      HttpServletResponse response,
248                                      ActionMapping mapping)
249         throws IOException, ServletException {
250 
251         if (log.isTraceEnabled()) {
252             log.trace("Performing standard forward handling");
253         }
254         boolean result = super.processForward
255             (request, response, mapping);
256         if (log.isDebugEnabled()) {
257             log.debug("Standard forward handling returned " + result);
258         }
259         return (result);
260 
261     }
262 
263 
264     // Override default processing to provide logging
265     protected void processForwardConfig(HttpServletRequest request,
266                                         HttpServletResponse response,
267                                         ForwardConfig forward)
268         throws IOException, ServletException {
269 
270         if (log.isTraceEnabled()) {
271             log.trace("Performing standard forward config handling");
272         }
273         super.processForwardConfig(request, response, forward);
274         if (log.isDebugEnabled()) {
275             log.debug("Standard forward config handling completed");
276         }
277 
278     }
279 
280 
281     // Override default processing to provide logging
282     protected boolean processInclude(HttpServletRequest request,
283                                      HttpServletResponse response,
284                                      ActionMapping mapping)
285         throws IOException, ServletException {
286 
287         if (log.isTraceEnabled()) {
288             log.trace("Performing standard include handling");
289         }
290         boolean result = super.processInclude
291             (request, response, mapping);
292         if (log.isDebugEnabled()) {
293             log.debug("Standard include handling returned " + result);
294         }
295         return (result);
296 
297     }
298 
299 
300     /***
301      * <p>Identify and return the path component (from the request URI for a
302      * non-Faces request, or from the form event for a Faces request)
303      * that we will use to select an ActionMapping to dispatch with.
304      * If no such path can be identified, create an error response and return
305      * <code>null</code>.</p>
306      *
307      * @param request The servlet request we are processing
308      * @param response The servlet response we are creating
309      *
310      * @exception IOException if an input/output error occurs
311      */
312     protected String processPath(HttpServletRequest request,
313                                  HttpServletResponse response)
314         throws IOException {
315 
316         // Are we processing a Faces request?
317         ActionEvent event = (ActionEvent)
318             request.getAttribute(Constants.ACTION_EVENT_KEY);
319 
320         // Handle non-Faces requests in the usual way
321         if (event == null) {
322             if (log.isTraceEnabled()) {
323                 log.trace("Performing standard processPath() processing");
324             }
325             return (super.processPath(request, response));
326         }
327 
328         // Calculate the path from the form name
329         UIComponent component = event.getComponent();
330         if (log.isTraceEnabled()) {
331             log.trace("Locating form parent for command component " +
332                       event.getComponent());
333         }
334         while (!(component instanceof FormComponent)) {
335             component = component.getParent();
336             if (component == null) {
337                 log.warn("Command component was not nested in a Struts form!");
338                 return (null);
339             }
340         }
341         if (log.isDebugEnabled()) {
342             log.debug("Returning selected path of '" +
343                       ((FormComponent) component).getAction() + "'");
344         }
345         return (((FormComponent) component).getAction());
346 
347     }
348 
349 
350     /***
351      * <p>Populate the properties of the specified <code>ActionForm</code>
352      * instance from the request parameters included with this request,
353      * <strong>IF</strong> this is a non-Faces request.  For a Faces request,
354      * this will have already been done by the <em>Update Model Values</em>
355      * phase of the request processing lifecycle, so all we have to do is
356      * recognize whether the request was cancelled or not.</p>
357      *
358      * @param request The servlet request we are processing
359      * @param response The servlet response we are creating
360      * @param form The ActionForm instance we are populating
361      * @param mapping The ActionMapping we are using
362      *
363      * @exception ServletException if thrown by RequestUtils.populate()
364      */
365     protected void processPopulate(HttpServletRequest request,
366                                    HttpServletResponse response,
367                                    ActionForm form,
368                                    ActionMapping mapping)
369         throws ServletException {
370 
371         // Are we processing a Faces request?
372         ActionEvent event = (ActionEvent)
373             request.getAttribute(Constants.ACTION_EVENT_KEY);
374 
375         // Handle non-Faces requests in the usual way
376         if (event == null) {
377             if (log.isTraceEnabled()) {
378                 log.trace("Performing standard processPopulate() processing");
379             }
380             super.processPopulate(request, response, form, mapping);
381             return;
382         }
383 
384         // Faces Requests require no processing for form bean population
385         // so we need only check for the cancellation command name
386         if (log.isTraceEnabled()) {
387             log.trace("Faces request, so no processPopulate() processing");
388         }
389         UIComponent source = event.getComponent();
390         if (source instanceof UICommand) {
391             if ("cancel".equals(((UICommand) source).getId())) {
392                 if (log.isTraceEnabled()) {
393                     log.trace("Faces request with cancel button pressed");
394                 }
395                 request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
396             }
397         }
398 
399     }
400 
401 
402     // Override default processing to provide logging
403     protected boolean processValidate(HttpServletRequest request,
404                                       HttpServletResponse response,
405                                       ActionForm form,
406                                       ActionMapping mapping)
407         throws IOException, ServletException, InvalidCancelException {
408 
409         if (log.isTraceEnabled()) {
410             log.trace("Performing standard validation");
411         }
412         boolean result = super.processValidate
413             (request, response, form, mapping);
414         if (log.isDebugEnabled()) {
415             log.debug("Standard validation processing returned " + result);
416         }
417         return (result);
418 
419     }
420 
421 
422     // --------------------------------------------------------- Private Methods
423 
424 
425     /***
426      * <p>Return <code>true</code> if the specified context-relative URI
427      * specifies a request to be processed by the Struts controller servlet.</p>
428      *
429      * @param uri URI to be checked
430      */
431     private boolean isStrutsRequest(String uri) {
432 
433         int question = uri.indexOf("?");
434         if (question >= 0) {
435             uri = uri.substring(0, question);
436         }
437         String mapping = (String)
438             servlet.getServletContext().getAttribute(Globals.SERVLET_KEY);
439         if (mapping == null) {
440             return (false);
441         } else if (mapping.startsWith("*.")) {
442             return (uri.endsWith(mapping.substring(1)));
443         } else if (mapping.endsWith("/*")) {
444             return (uri.startsWith(mapping.substring(0, mapping.length() - 2)));
445         } else {
446             return (false);
447         }
448 
449     }
450 
451 
452 }