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.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      // ------------------------------------------------------ Instance Variables
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      // ------------------------------------------------------- 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 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     // Override default processing to provide logging
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     // Override default processing to provide logging
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     // Override default processing to provide logging
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     // Override default processing to provide logging
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     // Override default processing to provide logging
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     // Override default processing to provide logging
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         // Are we processing a Faces request?
331         ActionEvent event = (ActionEvent)
332             request.getAttribute(Constants.ACTION_EVENT_KEY);
333 
334         // Handle non-Faces requests in the usual way
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         // Calculate the path from the form name
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         // Are we processing a Faces request?
386         ActionEvent event = (ActionEvent)
387             request.getAttribute(Constants.ACTION_EVENT_KEY);
388 
389         // Handle non-Faces requests in the usual way
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         // Faces Requests require no processing for form bean population
399         // so we need only check for the cancellation command name
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     // Override default processing to provide logging
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     // --------------------------------------------------------- Private Methods
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 }