You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by ri...@apache.org on 2004/10/15 00:06:53 UTC

svn commit: rev 54806 - in incubator/beehive/trunk/netui/src: compiler/org/apache/beehive/netui/compiler compiler/org/apache/beehive/netui/compiler/grammar pageflow/org/apache/beehive/netui/pageflow pageflow/org/apache/beehive/netui/pageflow/annotations pageflow/org/apache/beehive/netui/pageflow/internal util/org/apache/beehive/netui/util

Author: rich
Date: Thu Oct 14 15:06:52 2004
New Revision: 54806

Added:
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/DoubleFormPostException.java   (contents, props changed)
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/DeferredPageFlowException.java   (contents, props changed)
Modified:
   incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/CompilerUtils.java
   incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/ForwardGrammar.java
   incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/MessageResourcesGrammar.java
   incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/UniqueValueType.java
   incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/WebappPathOrActionType.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ActionNotFoundException.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/FlowController.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ForwardHandler.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/LoginExpiredException.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/NotLoggedInException.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowManagedObjectException.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowRequestProcessor.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowUtils.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/UnfulfilledRolesException.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/annotations/Jpf.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/InternalUtils.java
   incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/RequestValues.java
   incubator/beehive/trunk/netui/src/util/org/apache/beehive/netui/util/netui.properties
Log:
- Fixed an NPE in PageFlowUtils.setOutputForm that occurred when:
- Fixed to remove overzealous 'Duplicate Forward' errors from the annotation processor where two @Jpf.Forward annotations are exactly the same.
- Added the beginnings of support for an attribute on @Jpf.Action to prevent double-posting of a form.  More later, when the feature is finished.
- Added a more general framework for deferring exceptions from PageFlowRequestProcessor until the point where they can be addressed by declarative exception handling in the page flow.

DRT/BVT: netui server (WinXP)
BB: self (linux)



Modified: incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/CompilerUtils.java
==============================================================================
--- incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/CompilerUtils.java	(original)
+++ incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/CompilerUtils.java	Thu Oct 14 15:06:52 2004
@@ -741,7 +741,7 @@
         return null;
     }
     
-    public static boolean annotationsAreEqual( AnnotationMirror a1, AnnotationMirror a2,
+    public static boolean annotationsAreEqual( AnnotationMirror a1, AnnotationMirror a2, boolean allowExactDuplicates,
                                                AnnotationProcessorEnvironment env ) 
     {
         assert a1 != null;
@@ -752,7 +752,8 @@
         // If this behavior changes, we want to rely on equals(), not this deep comparison, which is more expensive
         // and wrong if the two annotations 'look' exactly the same.
         //
-        if ( Boolean.parseBoolean( env.getOptions().get( USE_EQUALS_TO_COMPARE_ANNOTATIONS_ATTR ) ) )
+        if ( ! allowExactDuplicates
+             && Boolean.parseBoolean( env.getOptions().get( USE_EQUALS_TO_COMPARE_ANNOTATIONS_ATTR ) ) )
         {
             return a1.equals( a2 );
         }
@@ -791,7 +792,7 @@
                     if ( o1 instanceof AnnotationMirror )
                     {
                         if ( ! ( o2 instanceof AnnotationMirror ) ) return false;
-                        if ( ! annotationsAreEqual( ( AnnotationMirror ) o1, ( AnnotationMirror ) o2, env ) ) return false;
+                        if ( ! annotationsAreEqual( ( AnnotationMirror ) o1, ( AnnotationMirror ) o2, allowExactDuplicates, env ) ) return false;
                     }
                     else
                     {
@@ -802,7 +803,7 @@
             else if ( val1 instanceof AnnotationMirror )
             {
                 if ( ! ( val2 instanceof AnnotationMirror ) ) return false;
-                if ( ! annotationsAreEqual( ( AnnotationMirror ) val1, ( AnnotationMirror ) val2, env ) ) return false;
+                if ( ! annotationsAreEqual( ( AnnotationMirror ) val1, ( AnnotationMirror ) val2, allowExactDuplicates, env ) ) return false;
             }
             else if ( ! val1.equals( val2 ) )
             {

Modified: incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/ForwardGrammar.java
==============================================================================
--- incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/ForwardGrammar.java	(original)
+++ incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/ForwardGrammar.java	Thu Oct 14 15:06:52 2004
@@ -268,5 +268,11 @@
         {
             return CATCH_TAG_NAME;
         }
+
+        @Override
+        protected boolean allowExactDuplicates()
+        {
+            return true;
+        }
     }
 }

Modified: incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/MessageResourcesGrammar.java
==============================================================================
--- incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/MessageResourcesGrammar.java	(original)
+++ incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/MessageResourcesGrammar.java	Thu Oct 14 15:06:52 2004
@@ -78,7 +78,7 @@
             
             for ( AnnotationMirror peerAnnotation : peerAnnotations )
             {
-                if ( ! CompilerUtils.annotationsAreEqual(  annotation, peerAnnotation, getEnv() )
+                if ( ! CompilerUtils.annotationsAreEqual(  annotation, peerAnnotation, false, getEnv() )
                      && CompilerUtils.getString( peerAnnotation, BUNDLE_KEY_ATTR, false ).length() == 0 )
                 {
                     addError( annotation, "error.multiple-default-message-resources", BUNDLE_KEY_ATTR );

Modified: incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/UniqueValueType.java
==============================================================================
--- incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/UniqueValueType.java	(original)
+++ incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/UniqueValueType.java	Thu Oct 14 15:06:52 2004
@@ -116,7 +116,7 @@
         
         for ( AnnotationMirror annotation : annotationsToCheck )
         {
-            if ( ! CompilerUtils.annotationsAreEqual( annotation, parentAnnotation, getEnv() ) )
+            if ( ! CompilerUtils.annotationsAreEqual( annotation, parentAnnotation, allowExactDuplicates(), getEnv() ) )
             {
                 AnnotationValue valueToCheck =
                         CompilerUtils.getAnnotationValue( annotation, memberName, _checkDefaultValues );
@@ -148,5 +148,10 @@
                 }
             }
         }
+    }
+    
+    protected boolean allowExactDuplicates()
+    {
+        return false;
     }
 }

Modified: incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/WebappPathOrActionType.java
==============================================================================
--- incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/WebappPathOrActionType.java	(original)
+++ incubator/beehive/trunk/netui/src/compiler/org/apache/beehive/netui/compiler/grammar/WebappPathOrActionType.java	Thu Oct 14 15:06:52 2004
@@ -109,7 +109,7 @@
                     String name = CompilerUtils.getString( ann, NAME_ATTR, false );
                     
                     if ( actionName.equals( name )
-                         && ! CompilerUtils.annotationsAreEqual( ann, annotationToIgnore, env ) )
+                         && ! CompilerUtils.annotationsAreEqual( ann, annotationToIgnore, false, env ) )
                     {
                         return true;
                     }

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ActionNotFoundException.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ActionNotFoundException.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ActionNotFoundException.java	Thu Oct 14 15:06:52 2004
@@ -39,7 +39,7 @@
 
     protected String[] getMessageParts()
     {
-        String formDescrip = ( _form != null ? "(form " + _form.getClass().getName() + ") " : "" );
+        String formDescrip = _form != null ? "(form " + _form.getClass().getName() + ") " : "";
         return new String[]{ "Unable to find action " + formDescrip, " in Page Flow ", "." };
     }
     

Added: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/DoubleFormPostException.java
==============================================================================
--- (empty file)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/DoubleFormPostException.java	Thu Oct 14 15:06:52 2004
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.beehive.netui.pageflow;
+
+import org.apache.beehive.netui.pageflow.internal.DeferredPageFlowException;
+
+import javax.servlet.http.HttpServletResponse;
+
+public class DoubleFormPostException
+        extends DeferredPageFlowException
+{
+    public DoubleFormPostException( String actionName )
+    {
+        super( actionName );
+    }
+    
+    protected Object[] getMessageArgs()
+    {
+        return new Object[]{ getActionName(), getFlowControllerURI() };
+    }
+
+    protected String[] getMessageParts()
+    {
+        return new String[]{ "A double-post occurred for action ", " in page flow ", "." };
+    }
+    
+    public int getResponseErrorCode()
+    {
+        return HttpServletResponse.SC_BAD_REQUEST;
+    }
+}

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/FlowController.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/FlowController.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/FlowController.java	Thu Oct 14 15:06:52 2004
@@ -24,6 +24,7 @@
 import org.apache.beehive.netui.pageflow.internal.InternalUtils;
 import org.apache.beehive.netui.pageflow.internal.InternalExpressionUtils;
 import org.apache.beehive.netui.pageflow.internal.RequestValues;
+import org.apache.beehive.netui.pageflow.internal.DeferredPageFlowException;
 import org.apache.beehive.netui.pageflow.annotations.Jpf;import org.apache.beehive.netui.pageflow.handler.LoginHandler;
 import org.apache.beehive.netui.pageflow.handler.ExceptionsHandler;
 import org.apache.beehive.netui.util.cache.ClassLevelCache;
@@ -410,12 +411,13 @@
             // Check whether isLoginRequired=true for this action.
             //
             LoginHandler loginHandler = ContextCache.get( getServletContext() ).getLoginHandler();
-            if ( pfActionMapping != null )
+            
+            if ( pfActionMapping != null && pfActionMapping.isLoginRequired()
+                 && loginHandler.getUserPrincipal( request ) == null )
             {
-                if ( pfActionMapping.isLoginRequired() && loginHandler.getUserPrincipal( request ) == null )
-                {
-                    throw createNotLoggedInException( actionName, request );
-                }
+                NotLoggedInException ex = createNotLoggedInException( actionName, request );
+                ex.setFlowController( this );
+                return handleException( ex, mapping, actionName, form, request, response );
             }
             
             //
@@ -424,20 +426,12 @@
             // throw an exception here.  Doing it this way allows the user to handle the exception
             // through declarative exception-handling.
             //
-            if ( PageFlowRequestProcessor.roleCheckFailed( request, true ) )
+            DeferredPageFlowException deferredException = RequestValues.getDeferredException( request, true );
+            
+            if ( deferredException != null )
             {
-                Exception ex;
-                
-                if ( loginHandler.getUserPrincipal( request ) == null )
-                {
-                    ex = createNotLoggedInException( actionName, request );
-                }
-                else
-                {
-                    ex = new UnfulfilledRolesException( mapping.getRoleNames(), mapping.getRoles(), actionName, this );
-                }
-                
-                return handleException( ex, mapping, actionName, form, request, response );
+                deferredException.setFlowController( this );
+                return handleException( deferredException, mapping, actionName, form, request, response );
             }
             
             //
@@ -516,7 +510,7 @@
                                          servletContext, this );
     }
     
-    private NotLoggedInException createNotLoggedInException( String actionName, HttpServletRequest request )
+    static NotLoggedInException createNotLoggedInException( String actionName, HttpServletRequest request )
     {
         String requestedSessionID = request.getRequestedSessionId();
 
@@ -526,11 +520,11 @@
             
             if ( session == null || ! requestedSessionID.equals( session.getId() ) )
             {
-                return new LoginExpiredException( actionName, this );
+                return new LoginExpiredException( actionName );
             }
         }
         
-        return new NotLoggedInException( actionName, this );
+        return new NotLoggedInException( actionName );
     }
     
     synchronized void create( HttpServletRequest request, HttpServletResponse response, ServletContext servletContext )

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ForwardHandler.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ForwardHandler.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/ForwardHandler.java	Thu Oct 14 15:06:52 2004
@@ -203,42 +203,38 @@
         // Figure out what URI to return to, and set the original form in the request or session.
         //        
         ActionForward retFwd = prevPageInfo.getForward();
+        ActionMapping prevMapping = prevPageInfo.getMapping();
         
         //
         // Restore any forms that are specified by this Forward (overwrite the original forms).
         //
         if ( retFwd instanceof Forward )
         {
-            PageFlowUtils.setOutputForms( prevPageInfo.getMapping(),
-                                          ( Forward ) retFwd, request, false );
-            
+            PageFlowUtils.setOutputForms( prevMapping, ( Forward ) retFwd, request, false );
             InternalUtils.addActionOutputs( ( ( Forward ) retFwd ).getActionOutputs(), request, false );
         }
         
         //
-        // If the currently-posted form is of the right type, initialize the page with
-        // that (but we won't overwrite the form that was set above).
+        // If the user hit the previous page directly (without going through an action), prevMapping will be null.
         //
-        if ( currentForm != null )
+        if ( prevMapping != null )
         {
-            PageFlowUtils.setOutputForm( prevPageInfo.getMapping(), currentForm, request, false );
-        }
+            //
+            // If the currently-posted form is of the right type, initialize the page with that (but we don't overwrite
+            // the form that was set above).
+            //
+            if ( currentForm != null ) PageFlowUtils.setOutputForm( prevMapping, currentForm, request, false );
         
-        //
-        // Initialize the page with the original form it got forwarded (but we won't overwrite the form
-        // that was set above).
-        //
-        if ( prevPageInfo.getMapping() != null )
-        {
-            InternalUtils.setFormInScope( prevPageInfo.getMapping().getName(),
-                                          prevPageInfo.getForm(),
-                                          prevPageInfo.getMapping(), request, false );
+            //
+            // Initialize the page with the original form it got forwarded (but we don't overwrite the form that was
+            // set above).
+            //
+            InternalUtils.setFormInScope( prevMapping.getName(), prevPageInfo.getForm(), prevMapping, request, false );
         }
             
         //
-        // If we're forwarding to a page in a different pageflow, we need to make sure
-        // the returned ActionForward has the right module path, and that it has
-        // contextRelative=true.
+        // If we're forwarding to a page in a different pageflow, we need to make sure the returned ActionForward has
+        // the right module path, and that it has contextRelative=true.
         //
         if ( ! retFwd.getContextRelative() && flowController != currentPageFlow )
         {

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/LoginExpiredException.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/LoginExpiredException.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/LoginExpiredException.java	Thu Oct 14 15:06:52 2004
@@ -22,8 +22,14 @@
  * Exception thrown when {@link NotLoggedInException} would be thrown, <i>and</i> when the
  * current HttpServletRequest refers to a session that no longer exists.
  */ 
-public class LoginExpiredException extends NotLoggedInException
+public class LoginExpiredException
+        extends NotLoggedInException
 {    
+    public LoginExpiredException( String actionName )
+    {
+        super( actionName );
+    }
+    
     public LoginExpiredException( String actionName, FlowController fc )
     {
         super( actionName, fc );

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/NotLoggedInException.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/NotLoggedInException.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/NotLoggedInException.java	Thu Oct 14 15:06:52 2004
@@ -17,6 +17,10 @@
  */
 package org.apache.beehive.netui.pageflow;
 
+import org.apache.beehive.netui.pageflow.internal.DeferredPageFlowException;
+
+import javax.servlet.http.HttpServletResponse;
+
 
 /**
  * Exception thrown when:
@@ -29,11 +33,14 @@
  * If the requested session-ID is different than the current session-ID, the {@link LoginExpiredException}
  * will be thrown instead of the <code>NotLoggedInException</code>.
  */ 
-public class NotLoggedInException extends PageFlowException
+public class NotLoggedInException
+        extends DeferredPageFlowException
 {
-    /**
-     * Construct with no error message.
-     */ 
+    public NotLoggedInException( String actionName )
+    {
+        super( actionName );
+    }
+    
     public NotLoggedInException( String actionName, FlowController fc )
     {
         super( actionName, fc );
@@ -50,5 +57,10 @@
         {
             "Action ", " on Page Flow ", " requires a current user, but there is no logged-in user."
         };
+    }
+
+    public int getResponseErrorCode()
+    {
+        return HttpServletResponse.SC_BAD_REQUEST;
     }
 }

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowManagedObjectException.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowManagedObjectException.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowManagedObjectException.java	Thu Oct 14 15:06:52 2004
@@ -67,7 +67,17 @@
     {
         return _managedObject;
     }
-    
+
+    /**
+     * Set the related PageFlowManagedObject.
+     * 
+     * @param managedObject the {@link PageFlowManagedObject} associated with this exception.
+     */ 
+    protected void setManagedObject( PageFlowManagedObject managedObject )
+    {
+        _managedObject = managedObject;
+    }
+
     /**
      * Handle the error by writing a message to the response.
      * 

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowRequestProcessor.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowRequestProcessor.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowRequestProcessor.java	Thu Oct 14 15:06:52 2004
@@ -32,6 +32,7 @@
 import org.apache.struts.upload.MultipartRequestWrapper;
 import org.apache.struts.upload.MultipartRequestHandler;
 import org.apache.struts.util.RequestUtils;
+import org.apache.struts.util.TokenProcessor;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletRequest;
@@ -64,9 +65,11 @@
 import org.apache.beehive.netui.pageflow.internal.InternalUtils;
 import org.apache.beehive.netui.pageflow.internal.FlowControllerAction;
 import org.apache.beehive.netui.pageflow.internal.InternalConstants;
+import org.apache.beehive.netui.pageflow.internal.DeferredPageFlowException;
 import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
 import org.apache.beehive.netui.pageflow.scoping.ScopedRequest;
 import org.apache.beehive.netui.pageflow.handler.ForwardRedirectHandler;
+import org.apache.beehive.netui.pageflow.handler.LoginHandler;
 
 import static org.apache.beehive.netui.pageflow.internal.InternalConstants.*;
 import static org.apache.beehive.netui.pageflow.PageFlowConstants.*;
@@ -95,7 +98,6 @@
     private static final String ALREADY_GOT_ACTION_ATTR = ATTR_PREFIX + "prefetchedAction";
     private static final String MULTIPART_REQUEST_WRAPPER_ATTR = ATTR_PREFIX + "multipartWrapper";
     private static final String PAGEFLOW_SCOPED_FORM_NAME_ATTR = ATTR_PREFIX + "pageFlowScopedFormName";
-    private static final String PROCESS_ROLES_FAILED_ATTR = ATTR_PREFIX + "processRolesFailed";
     private static final String FLOW_CONTROLLER_ACTION_CLASSNAME = FlowControllerAction.class.getName();
     
 
@@ -119,12 +121,12 @@
 
         if ( className != null && className.equals( FLOW_CONTROLLER_ACTION_CLASSNAME ) )
         {
+            ServletContext servletContext = getServletContext();
             String flowControllerClassName = actionMapping.getParameter();
             assert flowControllerClassName != null : FLOW_CONTROLLER_ACTION_CLASSNAME +
                                                      " requires the flow controller classname as its parameter.";
             try
             {
-                ServletContext servletContext = getServletContext();
                 Class flowControllerClass =
                         InternalUtils.getReloadableClass( flowControllerClassName, request, servletContext );
                 FlowController flowController =
@@ -135,7 +137,8 @@
             }
             catch ( ClassNotFoundException e )
             {
-                _log.error( "Could not find class " + className, e );
+                _log.error( "Could not find class " + flowControllerClassName, e );
+                return null;
             }
         }
         else
@@ -145,11 +148,11 @@
             // processRoles() in the same way that Struts would have acted, namely, by
             // sending an HTTP error.
             //
-            if ( request.getAttribute( PROCESS_ROLES_FAILED_ATTR ) != null )
+            DeferredPageFlowException deferredEx = RequestValues.getDeferredException( request, true );
+            
+            if ( deferredEx != null )
             {
-                request.removeAttribute( PROCESS_ROLES_FAILED_ATTR );
-                response.sendError( HttpServletResponse.SC_BAD_REQUEST,
-                                    getInternal().getMessage( "notAuthorized", actionMapping.getPath() ) );
+                response.sendError( deferredEx.getResponseErrorCode(), deferredEx.getResponseErrorMessage() );
                 return null;
             }
         }
@@ -250,10 +253,10 @@
                                             ActionMapping mapping )
     {
         //
-        // We're deferring role-check failures until the action phase.  But if the role check failed, we need to avoid
+        // We're deferring some failures until the action phase.  But if there was a failure, we need to avoid
         // doing anything here.
         //
-        if ( roleCheckFailed( request, false ) ) return null;
+        if ( RequestValues.getDeferredException( request, false ) != null ) return null;
         
         //
         // See if we're using a pageflow-scoped form (a member variable in the current pageflow).
@@ -359,10 +362,10 @@
         throws ServletException
     {
         //
-        // We're deferring role-check failures until the action phase.  But if the role check failed, we need to avoid
+        // We're deferring some failures until the action phase.  But if there was a failure, we need to avoid
         // doing anything here.
         //
-        if ( roleCheckFailed( request, false ) ) return;
+        if ( RequestValues.getDeferredException( request, false ) != null ) return;
         
         //
         // If a previous action forwarded us a form, use that -- don't populate it from request parameters.
@@ -409,13 +412,14 @@
         }
     }
 
-    protected boolean processValidate( HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping ) throws IOException, ServletException
+    protected boolean processValidate( HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping )
+            throws IOException, ServletException
     {
         //
-        // We're deferring role-check failures until the action phase.  But if the role check failed, we need to avoid
+        // We're deferring some failures until the action phase.  But if there was a failure, we need to avoid
         // doing anything here.
         //
-        if ( ! roleCheckFailed( request, false ) )
+        if ( RequestValues.getDeferredException( request, false ) == null )
         {
             return super.processValidate( request, response, form, mapping );
         }
@@ -425,13 +429,14 @@
         }
     }
 
-    protected boolean processForward( HttpServletRequest request, HttpServletResponse response, ActionMapping mapping ) throws IOException, ServletException
+    protected boolean processForward( HttpServletRequest request, HttpServletResponse response, ActionMapping mapping )
+            throws IOException, ServletException
     {
         //
-        // We're deferring role-check failures until the action phase.  But if the role check failed, we need to avoid
+        // We're deferring some failures until the action phase.  But if there was a failure, we need to avoid
         // doing anything here.
         //
-        if ( ! roleCheckFailed( request, false ) )
+        if ( RequestValues.getDeferredException( request, false ) == null )
         {
             return super.processForward( request, response, mapping );
         }
@@ -444,10 +449,10 @@
     protected boolean processInclude( HttpServletRequest request, HttpServletResponse response, ActionMapping mapping ) throws IOException, ServletException
     {
         //
-        // We're deferring role-check failures until the action phase.  But if the role check failed, we need to avoid
+        // We're deferring some failures until the action phase.  But if there was a failure, we need to avoid
         // doing anything here.
         //
-        if ( ! roleCheckFailed( request, false ) )
+        if ( RequestValues.getDeferredException( request, false ) == null )
         {
             return super.processInclude( request, response, mapping );
         }
@@ -858,8 +863,22 @@
         return false;
     }
     
-    public ActionMapping processMapping( HttpServletRequest request, HttpServletResponse response,
-                                         String path )
+    private void checkTransaction( HttpServletRequest request, ActionMapping mapping )
+    {
+        //
+        // TODO: We haven't yet hooked up the support for marking an action as preventDoublePost=true.
+        //
+        if ( false )
+        {
+            if ( ! TokenProcessor.getInstance().isTokenValid( request, true ) )
+            {
+                DoubleFormPostException ex = new DoubleFormPostException( PageFlowUtils.getActionName( mapping ) );
+                RequestValues.setDeferredException( request, ex );
+            }
+        }
+    }
+    
+    public ActionMapping processMapping( HttpServletRequest request, HttpServletResponse response, String path )
         throws IOException
     {
         //
@@ -885,6 +904,7 @@
                                + ", form " + forwardedFormClassName );
                 }
                 
+                checkTransaction( request, mapping );
                 return mapping;
             }
         }
@@ -915,6 +935,7 @@
             if ( ! wrongForm )
             {
                 request.setAttribute( Globals.MAPPING_KEY, mapping );
+                checkTransaction( request, mapping );
                 return mapping;
             }
         }
@@ -929,6 +950,7 @@
             {
                 mapping = ( ActionMapping ) configs[i];
                 request.setAttribute( Globals.MAPPING_KEY, mapping );
+                checkTransaction( request, mapping );
                 return mapping;
             }
         }
@@ -1086,15 +1108,21 @@
         // Here, Struts sends an HTTP error.  We just store the result in the request, so
         // logic in FlowController can generate an exception at the right time.
         //
-        request.setAttribute( PROCESS_ROLES_FAILED_ATTR, Boolean.TRUE );
+        LoginHandler loginHandler = ContextCache.get( getServletContext() ).getLoginHandler();
+        String actionName = PageFlowUtils.getActionName( mapping );
+        DeferredPageFlowException ex;
+        
+        if ( loginHandler.getUserPrincipal( request ) == null )
+        {
+            ex = FlowController.createNotLoggedInException( actionName, request );
+        }
+        else
+        {
+            ex = new UnfulfilledRolesException( mapping.getRoleNames(), mapping.getRoles(), actionName );
+        }
+                        
+        RequestValues.setDeferredException( request, ex );
         return true;    // We'll fail later, in processActionCreate.
-    }
-    
-    static boolean roleCheckFailed( HttpServletRequest request, boolean removeFlag )
-    {
-        boolean result = request.getAttribute( PROCESS_ROLES_FAILED_ATTR ) != null;
-        if ( removeFlag ) request.removeAttribute( PROCESS_ROLES_FAILED_ATTR );
-        return result;
     }
     
     private static String addScopeParams( String url, HttpServletRequest request )

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowUtils.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowUtils.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/PageFlowUtils.java	Thu Oct 14 15:06:52 2004
@@ -323,15 +323,12 @@
     public static void setOutputForms( ActionMapping mapping, Forward fwd, HttpServletRequest request, 
                                        boolean overwrite )
     {
-        if ( fwd == null )
-        {
-            return;
-        }
+        if ( fwd == null ) return;
         
-        if ( mapping != null )
-        {
-            setOutputForms( mapping, fwd.getOutputForms(), request, overwrite );
-        }
+        //
+        // *If* there is a target action mapping, set output forms for it.
+        //
+        if ( mapping != null ) setOutputForms( mapping, fwd.getOutputForms(), request, overwrite );
         
         RequestValues.setForwardedForm( request, fwd.getFirstOutputForm( request ) );
     }

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/UnfulfilledRolesException.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/UnfulfilledRolesException.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/UnfulfilledRolesException.java	Thu Oct 14 15:06:52 2004
@@ -17,13 +17,19 @@
  */
 package org.apache.beehive.netui.pageflow;
 
+import org.apache.beehive.netui.pageflow.internal.DeferredPageFlowException;
+import org.apache.beehive.netui.util.Bundle;
+
+import javax.servlet.http.HttpServletResponse;
+
 
 /**
  * Exception that is thrown when an action method marked with <code>@jpf:action
  * roles-allowed="</code><i>roles</i><code>"</code> is hit when there is a logged-in user
  * who does not fulfil any of the given roles.
  */ 
-public class UnfulfilledRolesException extends PageFlowException
+public class UnfulfilledRolesException
+        extends DeferredPageFlowException
 {
     private String[] _roleNames;
     private String _rolesList;
@@ -34,10 +40,9 @@
      * 
      * @param roleNames an array of String role names.
      */ 
-    public UnfulfilledRolesException( String[] roleNames, String rolesList, String actionName,
-                                      FlowController fc )
-    {
-        super( actionName, fc );
+    public UnfulfilledRolesException( String[] roleNames, String rolesList, String actionName)
+     {
+        super( actionName );
         _roleNames = roleNames;
         _rolesList = rolesList;
     }
@@ -64,5 +69,15 @@
         {
             "Action ", " on Page Flow ", " requires the user to be in one of the following roles: ", "."
         };
+    }
+
+    public String getResponseErrorMessage()
+    {
+        return Bundle.getString( "PageFlow_UnfulfilledRolesException_ResponseMessage", getActionName() );
+    }
+
+    public int getResponseErrorCode()
+    {
+        return HttpServletResponse.SC_BAD_REQUEST;
     }
 }

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/annotations/Jpf.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/annotations/Jpf.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/annotations/Jpf.java	Thu Oct 14 15:06:52 2004
@@ -305,8 +305,7 @@
         /**
          * the type of exception to catch (required )
          */
-         // @TODO when javelinx supports it, change this to Class<? extends java.lang.Throwable> type();
-        Class type();
+        Class<? extends Throwable> type();
 
         /**
          * the Jpf.ExceptionHandler method to invoke when the exception occurs (optional )

Added: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/DeferredPageFlowException.java
==============================================================================
--- (empty file)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/DeferredPageFlowException.java	Thu Oct 14 15:06:52 2004
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.beehive.netui.pageflow.internal;
+
+import org.apache.beehive.netui.pageflow.PageFlowException;
+import org.apache.beehive.netui.pageflow.FlowController;
+
+
+public abstract class DeferredPageFlowException
+        extends PageFlowException
+{
+    protected DeferredPageFlowException( String actionName )
+    {
+        super( actionName, null );
+    }
+    
+    protected DeferredPageFlowException( String actionName, FlowController fc )
+    {
+        super( actionName, fc );
+    }
+
+    public FlowController getFlowController()
+    {
+        assert super.getFlowController() != null : "setFlowController() was never called for DeferredPageFlowException";
+        return super.getFlowController();
+    }
+
+    public void setFlowController( FlowController fc )
+    {
+        setManagedObject( fc );
+    }
+    
+    public abstract int getResponseErrorCode();
+    
+    public String getResponseErrorMessage()
+    {
+        return getLocalizedMessage();
+    }
+}

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/InternalUtils.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/InternalUtils.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/InternalUtils.java	Thu Oct 14 15:06:52 2004
@@ -65,7 +65,6 @@
     private static final String APACHE_XMLOBJECT_CLASSNAME = "org.apache.xmlbeans.XmlObject";
     private static final Class BEA_XMLOBJECT_CLASS = loadClassNonFatal( BEA_XMLOBJECT_CLASSNAME );
     private static final Class APACHE_XMLOBJECT_CLASS = loadClassNonFatal( APACHE_XMLOBJECT_CLASSNAME );
-    private static final String FORWARDED_OUTPUT_FORM_ATTR = ATTR_PREFIX + "forwardedForm";    
     private static final String SINGLETON_PAGEFLOWS_ATTR_PREFIX = ATTR_PREFIX + "singletonPageFlow:";
     private static final String ACTIONOUTPUT_MAP_ATTR = ATTR_PREFIX + "actionOutputs";
     private static final String BINDING_UPDATE_ERRORS_ATTR = ATTR_PREFIX + "bindingUpdateErrors";

Modified: incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/RequestValues.java
==============================================================================
--- incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/RequestValues.java	(original)
+++ incubator/beehive/trunk/netui/src/pageflow/org/apache/beehive/netui/pageflow/internal/RequestValues.java	Thu Oct 14 15:06:52 2004
@@ -33,6 +33,7 @@
     private static final String AVOID_DIRECT_RESPONSE_OUTPUT_ATTR = ATTR_PREFIX + "_avoidDirectResponseOutput";
     private static final String ORIGINAL_REQUEST_URI_ATTR = ATTR_PREFIX + "origRequestURI";
     private static final String FORWARDED_FORMBEAN_ATTR = ATTR_PREFIX + "forwardedForm";
+    private static final String DEFERRED_EXCEPTION_ATTR = ATTR_PREFIX + "deferredException";
     
     
     public static boolean isForwardedRequest( ServletRequest request )
@@ -99,12 +100,22 @@
     public static ActionForm getForwardedForm( ServletRequest request, boolean removeFromRequest )
     {
         ActionForm form = ( ActionForm ) request.getAttribute( FORWARDED_FORMBEAN_ATTR );
-        
-        if ( removeFromRequest )
-        {
-            request.removeAttribute( FORWARDED_FORMBEAN_ATTR );
-        }
-        
+        if ( removeFromRequest ) request.removeAttribute( FORWARDED_FORMBEAN_ATTR );
         return form;
+    }
+    
+    /**
+     * This is an error deferred until FlowController.execute().
+     */ 
+    public static void setDeferredException( ServletRequest request, DeferredPageFlowException e )
+    {
+        request.setAttribute( DEFERRED_EXCEPTION_ATTR, e );
+    }
+    
+    public static DeferredPageFlowException getDeferredException( ServletRequest request, boolean removeFromRequest )
+    {
+        DeferredPageFlowException e = ( DeferredPageFlowException ) request.getAttribute( DEFERRED_EXCEPTION_ATTR );
+        if ( removeFromRequest ) request.removeAttribute( DEFERRED_EXCEPTION_ATTR );
+        return e;
     }
 }

Modified: incubator/beehive/trunk/netui/src/util/org/apache/beehive/netui/util/netui.properties
==============================================================================
--- incubator/beehive/trunk/netui/src/util/org/apache/beehive/netui/util/netui.properties	(original)
+++ incubator/beehive/trunk/netui/src/util/org/apache/beehive/netui/util/netui.properties	Thu Oct 14 15:06:52 2004
@@ -328,6 +328,8 @@
 PageFlow_UnfulfilledRolesException_Message= \
 Action {0} in page flow {1} requires the user to be in one of the following roles: {2}.
 
+PageFlow_UnfulfilledRolesException_ResponseMessage = User is not authorized to access action {0}.
+
 PageFlow_NoMatchingActionMethodException_Page= \
 <html><head><title>Page Flow Error - No Matching Action Method</title></head>\n \
 <body>\n \
@@ -431,6 +433,20 @@
 
 PageFlow_ActionNotFoundException_Message= \
 Unable to find matching action {0} in page flow {1}.
+
+PageFlow_DoubleFormPostException_Page= \
+<html><head><title>Page Flow Error - Double Form Post</title></head>\n \
+<body>\n \
+<h1>Page Flow Error - Double Form Post</h1>\n \
+<table border="1" cellspacing="0">\n \
+<tr><td><b>Page Flow:</b></td><td>{1}</td></tr>\n \
+<tr><td><b>Action:</b></td><td>{0}</td></tr>\n \
+</table><br />\n \
+<span style="color:red">A double-post occurred for action <b>{0}</b>.</span>\n \
+</body></html>\n
+
+PageFlow_DoubleFormPostException_Message= \
+A double-post occurred for action <b>{0}</b> in page flow {1}.
 
 PageFlow_UnhandledException_Page = \
 <html><head><title>Page Flow Unhandled Exception</title></head>\n \