KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > netui > pageflow > PageFlowController


1 /*
2  * Copyright 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  * $Header:$
17  */

18 package org.apache.beehive.netui.pageflow;
19
20 import org.apache.beehive.netui.util.internal.InternalStringBuilder;
21
22 import javax.servlet.http.HttpServletRequest JavaDoc;
23 import javax.servlet.http.HttpServletResponse JavaDoc;
24 import javax.servlet.http.HttpSessionBindingEvent JavaDoc;
25 import javax.servlet.ServletContext JavaDoc;
26 import java.lang.reflect.Field JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.apache.struts.action.ActionForm;
30 import org.apache.struts.action.ActionMapping;
31 import org.apache.struts.action.ActionForward;
32 import org.apache.struts.config.ModuleConfig;
33 import org.apache.struts.config.ControllerConfig;
34 import org.apache.beehive.netui.util.logging.Logger;
35 import org.apache.beehive.netui.util.internal.FileUtils;
36 import org.apache.beehive.netui.util.internal.DiscoveryUtils;
37 import org.apache.beehive.netui.util.internal.cache.ClassLevelCache;
38 import org.apache.beehive.netui.pageflow.config.PageFlowControllerConfig;
39 import org.apache.beehive.netui.pageflow.internal.CachedPageFlowInfo;
40 import org.apache.beehive.netui.pageflow.internal.InternalUtils;
41 import org.apache.beehive.netui.pageflow.internal.InternalConstants;
42 import org.apache.beehive.netui.pageflow.internal.AdapterManager;
43 import org.apache.beehive.netui.pageflow.internal.CachedSharedFlowRefInfo;
44 import org.apache.beehive.netui.pageflow.internal.ViewRenderer;
45 import org.apache.beehive.netui.pageflow.internal.PageFlowRequestWrapper;
46 import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
47
48
49 /**
50  * <p>
51  * Base class for controller logic, exception handlers, and state associated with a particular web directory path.
52  * The class is configured through the
53  * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller} annotation.
54  * </p>
55  *
56  * <p>
57  * When a page flow request (the page flow URI itself, or any ".do" or page URI in the directory path), arrives, an
58  * instance of the associated PageFlowController class is set as the <i>current page flow</i>, and remains stored in the
59  * session until a different one becomes active ("long lived" page flows stay in the session indefinitely;
60  * see {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#longLived longLived}
61  * on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}).
62  * </p>
63  *
64  * <p>
65  * The page flow class handles <i>actions</i> that are most commonly raised by user interaction with pages. The actions
66  * are handled by <i>action methods</i> or <i>action annotations</i> that determine the next URI to be displayed, after
67  * optionally performing arbitrary logic.
68  * </p>
69  *
70  * <p>
71  * If the PageFlowController is a "nested page flow"
72  * ({@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#nested nested} is set to <code>true</code>
73  * on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}), then this
74  * is a reusable, modular flow that can be "nested" during other flows. It has entry points (actions with optional form
75  * bean arguments), and exit points ({@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
76  * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
77  * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward} annotations
78  * that have <code>returnAction</code> attributes).
79  * </p>
80  *
81  * <p>
82  * The page flow class also handles exceptions thrown by actions or during page execution
83  * (see {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#catches catches} on
84  * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}). Unhandled exceptions are
85  * handled in order by declared {@link SharedFlowController}s
86  * (see {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs} on
87  * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}).
88  * </p>
89  *
90  * <p>
91  * Properties in the current page flow instance can be accessed from JSP 2.0-style expressions like this one:
92  * <code>${pageFlow.someProperty}</code>.
93  * </p>
94  *
95  * <p>
96  * There may only be one page flow in any package.
97  * </p>
98  *
99  * @see SharedFlowController
100  */

101 public abstract class PageFlowController
102         extends FlowController
103         implements InternalConstants
104 {
105     /**
106      * A 'return-to="page"' forward brings the user back to the previous page. This object
107      * stores information about the current state of affairs, such as the origin URI and
108      * its ActionForm.
109      */

110     private PreviousPageInfo _previousPageInfo = null;
111     private PreviousPageInfo _currentPageInfo = null;
112     
113     /**
114      * A 'return-to="action"' forward reruns the previous action. This object stores the previous
115      * action URI and its ActionForm.
116      */

117     private PreviousActionInfo _previousActionInfo;
118     
119     private boolean _isOnNestingStack = false;
120     private ViewRenderer _returnActionViewRenderer = null;
121     
122     private static final String JavaDoc REMOVING_PAGEFLOW_ATTR = InternalConstants.ATTR_PREFIX + "removingPageFlow";
123     private static final String JavaDoc SAVED_PREVIOUS_PAGE_INFO_ATTR = InternalConstants.ATTR_PREFIX + "savedPrevPageInfo";
124     private static final String JavaDoc CACHED_INFO_KEY = "cachedInfo";
125     private static final Logger _log = Logger.getInstance( PageFlowController.class );
126   
127     
128     /**
129      * Default constructor.
130      */

131     protected PageFlowController()
132     {
133     }
134     
135     /**
136      * Get the Struts module path for this page flow.
137      *
138      * @return a String that is the Struts module path for this controller, and which is also
139      * the directory path from the web application root to this PageFlowController
140      * (not including the action filename).
141      */

142     public String JavaDoc getModulePath()
143     {
144         return getCachedInfo().getModulePath();
145     }
146
147     /**
148      * Get the URI for addressing this PageFlowController.
149      *
150      * @return a String that is the URI which will execute the begin action on this
151      * PageFlowController.
152      */

153     public String JavaDoc getURI()
154     {
155         return getCachedInfo().getURI();
156     }
157     
158     /**
159      * Tell whether this PageFlowController can be "nested", i.e., if it can be invoked from another page
160      * flow with the intention of returning to the original one. Page flows are declared to be nested by specifying
161      * <code>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#nested nested}=true</code> on the
162      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller} annotation.
163      *
164      * @return <code>true</code> if this PageFlowController can be nested.
165      */

166     protected boolean isNestable()
167     {
168         return InternalUtils.isNestable( getModuleConfig() );
169     }
170     
171     /**
172      * Tell whether this is a "long lived" page flow. Once it is invoked, a long lived page flow is never
173      * removed from the session unless {@link #remove} is called. Navigating to another page flow hides
174      * the current long lived controller, but does not remove it.
175      */

176     protected boolean isLongLived()
177     {
178         return InternalUtils.isLongLived( getModuleConfig() );
179     }
180     
181     /**
182      * Remove this instance from the session. When inside a page flow action, {@link #remove} may be called instead.
183      */

184     protected synchronized void removeFromSession( HttpServletRequest JavaDoc request )
185     {
186         // This request attribute is used in persistInSession to prevent re-saving of this instance.
187
request.setAttribute( REMOVING_PAGEFLOW_ATTR, this );
188         
189         if ( isLongLived() )
190         {
191             PageFlowUtils.removeLongLivedPageFlow( getModulePath(), request );
192         }
193         else
194         {
195             InternalUtils.removeCurrentPageFlow( request );
196         }
197     }
198     
199     /**
200      * Tell whether this is a PageFlowController.
201      *
202      * @return <code>true</code>.
203      */

204     public boolean isPageFlow()
205     {
206         return true;
207     }
208     
209     /**
210      * Store this object in the user session, in the appropriate place. Used by the framework; normally should not be
211      * called directly.
212      */

213     public void persistInSession( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
214     {
215         PageFlowController currentPageFlow = PageFlowUtils.getCurrentPageFlow( request );
216         
217         if ( currentPageFlow != null && ! currentPageFlow.isOnNestingStack() )
218         {
219             //
220
// We're going to be implicitly destroying the current page flow. Synchronize on it so we don't mess
221
// with concurrent requests.
222
//
223
synchronized ( currentPageFlow )
224             {
225                 InternalUtils.setCurrentPageFlow( this, request );
226             }
227         }
228         else
229         {
230             InternalUtils.setCurrentPageFlow( this, request );
231         }
232     }
233
234     /**
235      * Ensures that any changes to this object will be replicated in a cluster (for failover),
236      * even if the replication scheme uses a change-detection algorithm that relies on
237      * HttpSession.setAttribute to be aware of changes. Note that this method is used by the framework
238      * and does not need to be called explicitly in most cases.
239      *
240      * @param request the current HttpServletRequest
241      */

242     public void ensureFailover( HttpServletRequest JavaDoc request )
243     {
244         //
245
// remove() puts the pageflow instance into a request attribute. Make sure not to re-save this
246
// instance if it's being removed. Also, if the session is null (after having been invalidated
247
// by the user), don't recreate it.
248
//
249
if ( request.getAttribute( REMOVING_PAGEFLOW_ATTR ) != this && request.getSession( false ) != null )
250         {
251             HttpServletRequest JavaDoc unwrappedRequest = PageFlowUtils.unwrapMultipart( request );
252             ServletContainerAdapter servletContainerAdapter = AdapterManager.getServletContainerAdapter( getServletContext() );
253         
254             //
255
// If this is a long-lived page flow, there are two attributes to deal with.
256
//
257
if ( isLongLived() )
258             {
259                 String JavaDoc longLivedAttrName = InternalUtils.getLongLivedFlowAttr( getModulePath() );
260                 longLivedAttrName = ScopedServletUtils.getScopedSessionAttrName( longLivedAttrName, unwrappedRequest );
261                 String JavaDoc currentLongLivedAttrName =
262                     ScopedServletUtils.getScopedSessionAttrName( CURRENT_LONGLIVED_ATTR, unwrappedRequest );
263                 servletContainerAdapter.ensureFailover( longLivedAttrName, this, unwrappedRequest );
264                 servletContainerAdapter.ensureFailover( currentLongLivedAttrName, getModulePath(), unwrappedRequest );
265             }
266             else
267             {
268                 String JavaDoc attrName = ScopedServletUtils.getScopedSessionAttrName( CURRENT_JPF_ATTR, unwrappedRequest );
269                 servletContainerAdapter.ensureFailover( attrName, this, unwrappedRequest );
270             }
271         }
272     }
273
274     /**
275      * @exclude
276      */

277     protected ActionForward internalExecute( ActionMapping mapping, ActionForm form, HttpServletRequest JavaDoc request,
278                                              HttpServletResponse JavaDoc response )
279         throws Exception JavaDoc
280     {
281         initializeSharedFlowFields( request );
282         return super.internalExecute( mapping, form, request, response );
283     }
284     
285     private void initializeSharedFlowFields( HttpServletRequest JavaDoc request )
286     {
287         //
288
// Initialize the shared flow fields.
289
//
290
CachedSharedFlowRefInfo.SharedFlowFieldInfo[] sharedFlowMemberFields =
291                 getCachedInfo().getSharedFlowMemberFields();
292         
293         if ( sharedFlowMemberFields != null )
294         {
295             for ( int i = 0; i < sharedFlowMemberFields.length; i++ )
296             {
297                 CachedSharedFlowRefInfo.SharedFlowFieldInfo fi = sharedFlowMemberFields[i];
298                 Field JavaDoc field = fi.field;
299                 
300                 if ( fieldIsUninitialized( field ) )
301                 {
302                     Map JavaDoc/*< String, SharedFlowController >*/ sharedFlows = PageFlowUtils.getSharedFlows( request );
303                     String JavaDoc name = fi.sharedFlowName;
304                     SharedFlowController sf =
305                             name != null ?
306                             ( SharedFlowController ) sharedFlows.get( name ) :
307                             PageFlowUtils.getGlobalApp( request );
308                     
309                     if ( sf != null )
310                     {
311                         initializeField( field, sf );
312                     }
313                     else
314                     {
315                         _log.error( "Could not find shared flow with name \"" + fi.sharedFlowName
316                                     + "\" to initialize field " + field.getName() + " in " + getClass().getName() );
317                     }
318                 }
319             }
320         }
321     }
322     
323     /**
324      * Get the a map of shared flow name to shared flow instance.
325      * @return a Map of shared flow name (string) to shared flow instance ({@link SharedFlowController}).
326      */

327     public Map JavaDoc/*< String, SharedFlowController >*/ getSharedFlows()
328     {
329         return PageFlowUtils.getSharedFlows( getRequest() );
330     }
331     
332     /**
333      * Get a shared flow, based on its name as defined in this page flow's
334      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs}
335      * attribute on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}.
336      * To retrieve any shared flow based on its class name, use {@link PageFlowUtils#getSharedFlow}.
337      *
338      * @param sharedFlowName the name of the shared flow, as in this page flows's
339      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs}
340      * attribute on the {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}
341      * annotation.
342      * @return the {@link SharedFlowController} with the given name.
343      */

344     public SharedFlowController getSharedFlow( String JavaDoc sharedFlowName )
345     {
346         return ( SharedFlowController ) PageFlowUtils.getSharedFlows( getRequest() ).get( sharedFlowName );
347     }
348     
349     /**
350      * Remove a shared flow from the session, based on its name as defined in this page flow's
351      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs}
352      * attribute on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}.
353      * To remove any shared flow based on its class name, use {@link PageFlowUtils#removeSharedFlow}.
354      *
355      * @param sharedFlowName the name of the shared flow, as in this page flows's
356      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs}
357      * attribute on the {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller &#64;Jpf.Controller}
358      * annotation.
359      */

360     public void removeSharedFlow( String JavaDoc sharedFlowName )
361     {
362         SharedFlowController sf = getSharedFlow( sharedFlowName );
363         if ( sf != null ) sf.removeFromSession( getRequest() );
364     }
365     
366     /**
367      * This is a framework method for initializing a newly-created page flow, and should not normally be called
368      * directly.
369      */

370     public final synchronized void create( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response,
371                                            ServletContext JavaDoc servletContext )
372     {
373         reinitialize( request, response, servletContext );
374         initializeSharedFlowFields( request );
375         
376         if ( isNestable() )
377         {
378             //
379
// Initialize a ViewRenderer for exiting the nested page flow.
380
//
381
String JavaDoc vrClassName = request.getParameter( InternalConstants.RETURN_ACTION_VIEW_RENDERER_PARAM );
382             
383             if ( vrClassName != null )
384             {
385                 ViewRenderer vr =
386                         ( ViewRenderer ) DiscoveryUtils.newImplementorInstance( vrClassName, ViewRenderer.class );
387                 
388                 if ( vr != null )
389                 {
390                     vr.init( request );
391                     _returnActionViewRenderer = vr;
392                 }
393             }
394         }
395         
396         super.create( request, response, servletContext );
397     }
398     
399     /**
400      * Get the "resource taxonomy": a period-separated list that starts with the current
401      * web application name, continues through all of this PageFlowController's parent directories,
402      * and ends with this PageFlowController's class name.
403      */

404     protected String JavaDoc getTaxonomy()
405     {
406         assert getRequest() != null : "this method can only be called during execute()";
407         String JavaDoc contextPath = getRequest().getContextPath();
408         assert contextPath.startsWith( "/" ) : contextPath;
409         return contextPath.substring( 1 ) + '.' + getClass().getName();
410     }
411     
412     /**
413      * Get the submitted form bean from the most recent action execution in this PageFlowController.
414      * <p>
415      * <i>Note: if the current page flow does not contain a
416      * </i>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward}<i> or a
417      * </i>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}<i> with
418      * </i><code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousAction Jpf.NavigateTo.previousAction}</code><i>,
419      * then this method will always return </i><code>null</code><i> by default. To enable it in this
420      * situation, add the following method to the page flow:</i><br>
421      * <blockquote>
422      * <code>
423      * protected boolean alwaysTrackPreviousAction()<br>
424      * {<br>
425      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
426      * }<br>
427      * </code>
428      * </blockquote>
429      *
430      * @deprecated This method may return an <code>ActionForm</code> wrapper when the form bean type does not extend
431      * <code>ActionForm</code>. Use {@link #getPreviousFormBean} instead.
432      * @return the ActionForm instance from the most recent action execution, or <code>null</code>
433      * if there was no form bean submitted.
434      * @see #getPreviousPageInfo
435      * @see #getCurrentPageInfo
436      * @see #getPreviousActionInfo
437      * @see #getPreviousActionURI
438      * @see #getPreviousForwardPath
439      * @see #getCurrentForwardPath
440      */

441     protected ActionForm getPreviousForm()
442     {
443         checkPreviousActionInfoDisabled();
444         return _previousActionInfo != null ? _previousActionInfo.getForm() : null;
445     }
446     
447     /**
448      * Get the submitted form bean from the most recent action execution in this PageFlowController.
449      * <p>
450      * <i>Note: if the current page flow does not contain a
451      * </i>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward}<i> or a
452      * </i>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}<i> with
453      * </i><code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousAction Jpf.NavigateTo.previousAction}</code><i>,
454      * then this method will always return </i><code>null</code><i> by default. To enable it in this
455      * situation, add the following method to the page flow:</i><br>
456      * <blockquote>
457      * <code>
458      * protected boolean alwaysTrackPreviousAction()<br>
459      * {<br>
460      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
461      * }<br>
462      * </code>
463      * </blockquote>
464      *
465      * @return the form bean instance from the most recent action execution, or <code>null</code>
466      * if there was no form bean submitted.
467      * @see #getPreviousPageInfo
468      * @see #getCurrentPageInfo
469      * @see #getPreviousActionInfo
470      * @see #getPreviousActionURI
471      * @see #getPreviousForwardPath
472      * @see #getCurrentForwardPath
473      */

474     protected Object JavaDoc getPreviousFormBean()
475     {
476         checkPreviousActionInfoDisabled();
477         return _previousActionInfo != null ? InternalUtils.unwrapFormBean( _previousActionInfo.getForm() ) : null;
478     }
479     
480     /**
481      * Get the URI for the most recent action in this PageFlowController.
482      * <p>
483      * <i>Note: if the current page flow does not use a
484      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
485      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
486      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
487      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousAction previousAction}</code>,
488      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
489      * following method to the page flow:</i><br>
490      * <blockquote>
491      * <code>
492      * protected boolean alwaysTrackPreviousAction()<br>
493      * {<br>
494      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
495      * }<br>
496      * </code>
497      * </blockquote>
498      *
499      * @return a String that is the most recent URI.
500      * @see #getPreviousPageInfo
501      * @see #getCurrentPageInfo
502      * @see #getPreviousActionInfo
503      * @see #getPreviousFormBean
504      * @see #getPreviousForwardPath
505      * @see #getCurrentForwardPath
506      */

507     protected String JavaDoc getPreviousActionURI()
508     {
509         checkPreviousActionInfoDisabled();
510         return _previousActionInfo != null ? _previousActionInfo.getActionURI() : null;
511     }
512     
513     /**
514      * Get the webapp-relative URI for the most recent page (in this page flow) shown to the user.
515      * <p>
516      * <i>Note: if the current page flow does not use a
517      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
518      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
519      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
520      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage currentPage}</code>
521      * or <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousPage previousPage}</code>,
522      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
523      * following method to the page flow:</i><br>
524      * <blockquote>
525      * <code>
526      * protected boolean alwaysTrackPreviousPage()<br>
527      * {<br>
528      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
529      * }<br>
530      * </code>
531      * </blockquote>
532      *
533      * @return a String that is the URI path for the most recent page shown to the user.
534      * @see #getPreviousPageInfo
535      * @see #getCurrentPageInfo
536      * @see #getPreviousActionInfo
537      * @see #getPreviousActionURI
538      * @see #getPreviousFormBean
539      * @see #getPreviousForwardPath
540      */

541     public String JavaDoc getCurrentForwardPath()
542     {
543         PreviousPageInfo curPageInfo = getCurrentPageInfo();
544         String JavaDoc path = null;
545         
546         if ( curPageInfo != null )
547         {
548             ActionForward curForward = curPageInfo.getForward();
549             if ( curForward != null )
550             {
551                 if ( curForward.getContextRelative() )
552                 {
553                     path = curForward.getPath();
554                 }
555                 else
556                 {
557                     path = getModulePath() + curForward.getPath();
558                 }
559             }
560         }
561         return path;
562     }
563     
564     /**
565      * Get the webapp-relative URI for the previous page (in this page flow) shown to the user.
566      * The previous page is the one shown before the most recent page.
567      * <p>
568      * <i>Note: if the current page flow does not use a
569      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
570      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
571      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
572      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage currentPage}</code>
573      * or <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousPage previousPage}</code>,
574      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
575      * following method to the page flow:</i><br>
576      * <blockquote>
577      * <code>
578      * protected boolean alwaysTrackPreviousPage()<br>
579      * {<br>
580      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
581      * }<br>
582      * </code>
583      * </blockquote>
584      *
585      * @return a String that is the URI path for the previous page shown to the user.
586      * @see #getPreviousPageInfo
587      * @see #getCurrentPageInfo
588      * @see #getPreviousActionInfo
589      * @see #getPreviousActionURI
590      * @see #getPreviousFormBean
591      * @see #getCurrentForwardPath
592      */

593     protected String JavaDoc getPreviousForwardPath()
594     {
595         PreviousPageInfo prevPageInfo = getPreviousPageInfo();
596         
597         if ( prevPageInfo != null )
598         {
599             ActionForward prevForward = prevPageInfo.getForward();
600             return prevForward != null ? prevForward.getPath() : null;
601         }
602         else
603         {
604             return null;
605         }
606     }
607     
608     /**
609      * Get a legacy PreviousPageInfo.
610      * @deprecated This method will be removed without replacement in a future release.
611      */

612     public final PreviousPageInfo getPreviousPageInfoLegacy( PageFlowController curJpf, HttpServletRequest JavaDoc request )
613     {
614         if ( request.getAttribute( RETURNING_FROM_NESTING_ATTR ) != null )
615         {
616             return getCurrentPageInfo();
617         }
618         else
619         {
620             return getPreviousPageInfo();
621         }
622     }
623     
624     /**
625      * Get information about the most recent page (in this page flow) shown to the user.
626      * <p>
627      * <i>Note: if the current page flow does not use a
628      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
629      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
630      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
631      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage currentPage}</code>
632      * or <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousPage previousPage}</code>,
633      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
634      * following method to the page flow:</i><br>
635      * <blockquote>
636      * <code>
637      * protected boolean alwaysTrackPreviousPage()<br>
638      * {<br>
639      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
640      * }<br>
641      * </code>
642      * </blockquote>
643      *
644      * @return a PreviousPageInfo with information about the most recent page shown to the user.
645      * @see #getPreviousPageInfo
646      * @see #getPreviousActionInfo
647      * @see #getPreviousActionURI
648      * @see #getPreviousFormBean
649      * @see #getPreviousForwardPath
650      * @see #getCurrentForwardPath
651      */

652     public final PreviousPageInfo getCurrentPageInfo()
653     {
654         checkPreviousPageInfoDisabled();
655         
656         if ( _currentPageInfo != null )
657         {
658             // Allows it to reconstruct transient members after session failover
659
_currentPageInfo.reinitialize( this );
660         }
661         
662         return _currentPageInfo;
663     }
664     
665     /**
666      * Get information about the previous page (in this page flow) shown to the user. The previous
667      * page is the one shown before the most recent page.
668      * <p>
669      * <i>Note: if the current page flow does not use a
670      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
671      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
672      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
673      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage currentPage}</code>
674      * or <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousPage previousPage}</code>,
675      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
676      * following method to the page flow:</i><br>
677      * </blockquote>
678      *
679      * @return a PreviousPageInfo with information about the previous page shown to the user.
680      * @see #getCurrentPageInfo
681      * @see #getPreviousActionInfo
682      * @see #getPreviousActionURI
683      * @see #getPreviousFormBean
684      * @see #getPreviousForwardPath
685      * @see #getCurrentForwardPath
686      */

687     public final PreviousPageInfo getPreviousPageInfo()
688     {
689         checkPreviousPageInfoDisabled();
690         
691         PreviousPageInfo ret = _previousPageInfo != null ? _previousPageInfo : _currentPageInfo;
692         
693         if ( ret != null )
694         {
695             ret.reinitialize( this ); // Allows it to reconstruct transient members after session failover
696
}
697         
698         return ret;
699     }
700     
701     /**
702      * Get information about the most recent action run in this page flow.
703      * <p>
704      * <i>Note: if the current page flow does not use a
705      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward &#64;Jpf.Forward},
706      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction &#64;Jpf.SimpleAction}, or
707      * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.ConditionalForward &#64;Jpf.ConditionalForward}
708      * with <code>navigateTo={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousAction previousAction}</code>,
709      * then this method will always return </i><code>null</code><i> by default. To enable it in this situation, add the
710      * following method to the page flow:</i><br>
711      * <blockquote>
712      * <code>
713      * protected boolean alwaysTrackPreviousAction()<br>
714      * {<br>
715      * &nbsp;&nbsp;&nbsp;&nbsp;return true;<br>
716      * }<br>
717      * </code>
718      * </blockquote>
719      *
720      * @return a PreviousActionInfo with information about the most recent action run in this page flow.
721      * @see #getPreviousPageInfo
722      * @see #getCurrentPageInfo
723      * @see #getPreviousActionURI
724      * @see #getPreviousFormBean
725      * @see #getPreviousForwardPath
726      * @see #getCurrentForwardPath
727      */

728     public final PreviousActionInfo getPreviousActionInfo()
729     {
730         checkPreviousActionInfoDisabled();
731         return _previousActionInfo;
732     }
733     
734     private void checkPreviousActionInfoDisabled()
735     {
736         if ( isPreviousActionInfoDisabled() )
737         {
738             throw new IllegalStateException JavaDoc( "Previous action information has been disabled in this page flow. Override alwaysTrackPreviousAction() to enable it." );
739         }
740     }
741     
742     private void checkPreviousPageInfoDisabled()
743     {
744         if ( isPreviousPageInfoDisabled() )
745         {
746             throw new IllegalStateException JavaDoc( "Previous page information has been disabled in this page flow. Override alwaysTrackPreviousPage() to enable it." );
747         }
748     }
749     
750     /**
751      * Get the display name of this page flow.
752      * @return the display name (the URI) of this page flow.
753      */

754     public String JavaDoc getDisplayName()
755     {
756         return getURI();
757     }
758     
759     public boolean isPreviousActionInfoDisabled()
760     {
761         if ( alwaysTrackPreviousAction() ) return false;
762         
763         ModuleConfig mc = getModuleConfig();
764         ControllerConfig cc = mc.getControllerConfig();
765         return cc instanceof PageFlowControllerConfig && ( ( PageFlowControllerConfig ) cc ).isReturnToActionDisabled();
766     }
767     
768     public boolean isPreviousPageInfoDisabled()
769     {
770         if ( alwaysTrackPreviousPage() ) return false;
771         
772         ModuleConfig mc = getModuleConfig();
773         ControllerConfig cc = mc.getControllerConfig();
774         return cc instanceof PageFlowControllerConfig && ( ( PageFlowControllerConfig ) cc ).isReturnToPageDisabled();
775     }
776     
777     /**
778      * Called from {@link FlowController#execute}.
779      */

780     void savePreviousActionInfo( ActionForm form, HttpServletRequest JavaDoc request, ActionMapping mapping,
781                                  ServletContext JavaDoc servletContext )
782     {
783         //
784
// If previous-action is disabled (unused in this pageflow), just return.
785
//
786
if ( isPreviousActionInfoDisabled() ) return;
787         String JavaDoc actionURI = InternalUtils.getDecodedServletPath( request );
788         _previousActionInfo = new PreviousActionInfo( form, actionURI, request.getQueryString() );
789     }
790     
791     /**
792      * Store information about recent pages displayed. This is a framework-invoked method that should not normally be
793      * called directly.
794      */

795     public void savePreviousPageInfo( ActionForward forward, ActionForm form, ActionMapping mapping,
796                                       HttpServletRequest JavaDoc request, ServletContext JavaDoc servletContext,
797                                       boolean isSpecialForward )
798     {
799         if ( forward != null )
800         {
801             //
802
// If previous-page is disabled (unused in this pageflow), or if we've already saved prevous-page info in
803
// this request (for example, forward to foo.faces which forwards to foo.jsp), just return.
804
//
805
if ( request.getAttribute( SAVED_PREVIOUS_PAGE_INFO_ATTR ) != null || isPreviousPageInfoDisabled() ) return;
806             
807             String JavaDoc path = forward.getPath();
808             int queryPos = path.indexOf( '?' );
809             if ( queryPos != -1 ) path = path.substring( 0, queryPos );
810             
811             //
812
// If a form bean was generated in this request, add it to the most recent PreviousPageInfo, so when we
813
// go back to that page, the *updated* field values are restored (i.e., we don't revert to the values of
814
// the form that was passed into the page originally).
815
//
816
if ( form != null && _currentPageInfo != null )
817             {
818                 ActionForm oldForm = _currentPageInfo.getForm();
819                 if ( oldForm == null || oldForm.getClass().equals( form.getClass() ) )
820                 {
821                     _currentPageInfo.setForm( form );
822                     _currentPageInfo.setMapping( mapping );
823                 }
824             }
825             
826             //
827
// Only keep track of *pages* forwarded to -- not actions or pageflows.
828
//
829
if ( ! FileUtils.osSensitiveEndsWith( path, ACTION_EXTENSION ) )
830             {
831                 //
832
// Only save previous-page info if the page is within this pageflow.
833
//
834
if ( isLocalFile( forward ) ) // || PageFlowUtils.osSensitiveEndsWith( path, JPF_EXTENSION ) )
835
{
836                     _previousPageInfo = _currentPageInfo;
837                     _currentPageInfo = new PreviousPageInfo( forward, form, mapping, request.getQueryString() );
838                     request.setAttribute( SAVED_PREVIOUS_PAGE_INFO_ATTR, Boolean.TRUE );
839                 }
840             }
841         }
842     }
843     
844     private boolean isLocalFile( ActionForward forward )
845     {
846         String JavaDoc path = forward.getPath();
847         
848         if ( ! forward.getContextRelative() )
849         {
850             return path.indexOf( '/', 1 ) == -1; // all paths in Struts start with '/'
851
}
852         else
853         {
854             String JavaDoc modulePath = getModulePath();
855             
856             if ( ! path.startsWith( modulePath ) )
857             {
858                 return false;
859             }
860             else
861             {
862                 return path.indexOf( '/', modulePath.length() + 1 ) == -1;
863             }
864         }
865     }
866     
867     private boolean isOnNestingStack()
868     {
869         return _isOnNestingStack;
870     }
871     
872     /**
873      * Callback when this object is removed from the user session. Causes {@link #onDestroy} to be called. This is a
874      * framework-invoked method that should not normally be called indirectly.
875      */

876     public void valueUnbound( HttpSessionBindingEvent JavaDoc event )
877     {
878         //
879
// Unless this pageflow has been pushed onto the nesting stack, do the onDestroy() callback.
880
//
881
if ( ! _isOnNestingStack )
882         {
883             destroy( event.getSession() );
884         }
885     }
886     
887     void setIsOnNestingStack( boolean isOnNestingStack )
888     {
889         _isOnNestingStack = isOnNestingStack;
890     }
891         
892     ActionForward forwardTo( ActionForward fwd, ActionMapping mapping, HttpServletRequest JavaDoc request,
893                              HttpServletResponse JavaDoc response, String JavaDoc actionName, ModuleConfig altModuleConfig,
894                              ActionForm form, ServletContext JavaDoc servletContext )
895     {
896         ActionForward superFwd = super.forwardTo( fwd, mapping, request, response, actionName,
897                                                   altModuleConfig, form, servletContext );
898         
899         //
900
// Special case: the *only* way for a nested pageflow to nest itself is for it
901
// to forward to itself as a .jpf. Simply executing an action in the .jpf isn't
902
// enough, obviously, since it's impossible to tell whether it should be executed
903
// in the current pageflow or a new nested one.
904
//
905
if ( superFwd != null && InternalUtils.isNestable( getModuleConfig() ) )
906         {
907             boolean selfNesting = false;
908             
909             if ( superFwd.getContextRelative() )
910             {
911                 if ( superFwd.getPath().equals( getURI() ) )
912                 {
913                     selfNesting = true;
914                 }
915             }
916             else
917             {
918                 if ( superFwd.getPath().equals( getStrutsLocalURI() ) )
919                 {
920                     selfNesting = true;
921                 }
922             }
923             
924             if ( selfNesting )
925             {
926                 if ( _log.isDebugEnabled() )
927                 {
928                     _log.debug( "Self-nesting page flow " + getURI() );
929                 }
930                 
931                 try
932                 {
933                     // This will cause the right pageflow stack stuff to happen.
934
RequestContext rc = new RequestContext( request, response );
935                     FlowControllerFactory.get( getServletContext() ).createPageFlow( rc, getClass() );
936                 }
937                 catch ( IllegalAccessException JavaDoc e )
938                 {
939                     // This should never happen -- if we successfully created this page flow once, we can do it again.
940
assert false : e;
941                     _log.error( e );
942                 }
943                 catch ( InstantiationException JavaDoc e )
944                 {
945                     _log.error( "Could not create PageFlowController instance of type " + getClass().getName(), e );
946                 }
947             }
948         }
949         
950         return superFwd;
951     }
952     
953     private String JavaDoc getStrutsLocalURI()
954     {
955         String JavaDoc className = getClass().getName();
956         int lastDot = className.lastIndexOf( '.' );
957         InternalStringBuilder ret = new InternalStringBuilder( "/" );
958         return ret.append( className.substring( lastDot + 1 ) ).append( JPF_EXTENSION ).toString();
959     }
960     
961     private CachedPageFlowInfo getCachedInfo()
962     {
963         ClassLevelCache cache = ClassLevelCache.getCache( getClass() );
964         CachedPageFlowInfo info = ( CachedPageFlowInfo ) cache.getCacheObject( CACHED_INFO_KEY );
965         
966         if ( info == null )
967         {
968             info = new CachedPageFlowInfo( getClass(), getServletContext() );
969             cache.setCacheObject( CACHED_INFO_KEY, info );
970         }
971         
972         return info;
973     }
974
975     final void beforePage()
976     {
977         //
978
// We may need to save the previous page info if the page was called directly (not forwarded through an action)
979
// and we do not yet have the forward path in the page flow or it is a different path.
980
//
981
if ( ! isPreviousPageInfoDisabled() )
982         {
983             HttpServletRequest JavaDoc request = getRequest();
984             String JavaDoc relativeUri = InternalUtils.getDecodedServletPath( request );
985     
986             String JavaDoc path = getCurrentForwardPath();
987             if ( path == null || ! path.equals( relativeUri ) )
988             {
989                 ActionForward actionForward = new ActionForward( relativeUri );
990                 actionForward.setContextRelative( true );
991                 actionForward.setRedirect( false );
992                 savePreviousPageInfo( actionForward, null, null, request, getServletContext(), false );
993             }
994         }
995     }
996     
997     /**
998      * @exclude
999      */

1000    public ActionForward exitNesting( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, ActionMapping mapping,
1001                                      ActionForm form )
1002    {
1003        if ( _returnActionViewRenderer != null )
1004        {
1005            PageFlowRequestWrapper.get( request ).setViewRenderer( _returnActionViewRenderer );
1006        }
1007        
1008        PerRequestState prevState = setPerRequestState( new PerRequestState( request, response, mapping ) );
1009        
1010        try
1011        {
1012            onExitNesting();
1013        }
1014        catch ( Throwable JavaDoc th )
1015        {
1016            try
1017            {
1018                return handleException( th, mapping, form, request, response );
1019            }
1020            catch ( Exception JavaDoc e )
1021            {
1022                _log.error( "Exception thrown while handling exception.", e );
1023            }
1024        }
1025        finally
1026        {
1027            setPerRequestState( prevState );
1028        }
1029        
1030        return null;
1031    }
1032    
1033    /**
1034     * Callback that is invoked when this controller instance is exiting nesting (through a return action).
1035     * {@link FlowController#getRequest}, {@link FlowController#getResponse}, {@link FlowController#getSession}
1036     * may all be used during this method.
1037     */

1038    protected void onExitNesting()
1039            throws Exception JavaDoc
1040    {
1041    }
1042}
1043
Popular Tags