[jbossseam-issues] [JBoss JIRA] Updated: (JBSEAM-1822) BaseSeamTest does not properly emulateJsfLifecycle does not handle phase listeners per the jsf 1.2 spec.

Chris Rudd (JIRA) jira-events at lists.jboss.org
Mon Aug 20 02:09:01 EDT 2007


     [ http://jira.jboss.com/jira/browse/JBSEAM-1822?page=all ]

Chris Rudd updated JBSEAM-1822:
-------------------------------

    Description: 
Under the 1.2 JSF spec, all phase listeners are called with the after phase events reguarless of if an exception was thrown during the phase processing.

The resulting issue is that when Init.isTransactionMangementEnabled is true, and and an exception (or an AssertionError) is thrown from within the phase method, the SeamPhase listner does not get a chance to handle the condition and rollback the transaction. This leaves the transaction open, and all further tests run for that class are now "tainted" as there is a transaction running that will never be completed.

Wrapping code in the phase methods (restoreViewPhase,applyRequestValuesPhase,processValidationsPhase,updateModelValuesPhase,invokeApplicationsPhase, renderResponsePhase) like this will resolve the issue :

      private void renderResponsePhase() throws Exception
      {
         phases.beforePhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
                  MockLifecycle.INSTANCE));
+        try
+        {         
          updateConversationId();
         
          renderResponseBegun = true;
  
          renderResponse();
  
          renderResponseComplete = true;
  
          facesContext.getApplication().getStateManager().saveView(facesContext);
  
          updateConversationId();

 +      }
 +      finally
 +      {
         phases.afterPhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
                  MockLifecycle.INSTANCE));
+       }
      }


it may be cleaner to refactor the phase methods into PhaseExection classes. (remove firing of phase events from the phase methods )

	public class PhaseExecution {
		private PhaseId phaseId;
		
		public PhaseExecution(PhaseId phaseId)
		{
			this.phaseId = phaseId;
		}
		
		protected abstract void execute() throws Exception
		
		public void run() throws Exception {
			fireBefore();
			try
			{
				execute();
			}
			finally
			{
				afterPhase();
			}
		}
		
		protected void fireBefore()
		{
			 phases.beforePhase(new PhaseEvent(facesContext, phaseId,
	                  MockLifecycle.INSTANCE));
		}

		protected void fireAfter()
		{
			 phases.afterPhase(new PhaseEvent(facesContext, phaseId,
	                  MockLifecycle.INSTANCE));
		}
	}
	
	final private PhaseExcecution RESTORE_VIEW_PHASE= new PhaseExecution(PhaseId.RESTORE_VIEW) {
		protected void execute() throws Exception {
			restoreViewPhase();
		}
	};
	
	final private PhaseExcecution RENDER_RESPONSE_PHASE= new PhaseExecution(PhaseId.RENDER_RESPONSE) {
		protected void execute() throws Exception {
			renderResponsePhase();
		}
	};
	...
	

    /**
     * @return true if a response was rendered
     */
    private boolean emulateJsfLifecycle() throws Exception
    {
       RESTORE_VIEW_PHASE.run();
       if ( !isGetRequest() && !skipToRender() )
       {
          APPLY_REQUEST_VALUES_PHASE.run();
          if (!skipToRender())
          {
             PROCESS_VALIDATIONS_PHASE.run();
             if ( !skipToRender() )
             {
                UPDATE_MODEL_VALUES_PHASE.run();
                if ( !skipToRender() )
                {
                   INVOKE_APPLICATION_PHASE.run();
                }
             }
          }
       }
       
       if ( skipRender() )
       {
          // we really should look at redirect parameters here!
          return false;
       }
       else
       {
          RENDER_RESPONSE_PHASE.run();
          return true;
       }
    }


  was:
Under the 1.2 JSF spec, all phase listeners are called with the after phase events reguarless of if an exception was thrown during the phase processing.

The resulting issue is that when Init.isTransactionMangementEnabled is true, and and an exception (or an AssertionError) is thrown from within the phase method, the SeamPhase listner does not get a chance to handle the condition and rollback the transaction. This leaves the transaction open, and all further tests run for that class are now "tainted" as there is a transaction running that will never be completed.

Wrapping code in the phase methods (restoreViewPhase,applyRequestValuesPhase,processValidationsPhase,updateModelValuesPhase,invokeApplicationsPhase, renderResponsePhase) like this will resolve the issue :

      private void renderResponsePhase() throws Exception
      {
         phases.beforePhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
                  MockLifecycle.INSTANCE));
+        boolean hasError = true;
+        try
+        {         
          updateConversationId();
         
          renderResponseBegun = true;
  
          renderResponse();
  
          renderResponseComplete = true;
  
          facesContext.getApplication().getStateManager().saveView(facesContext);
  
          updateConversationId();

          hasError = false;
 +      }
 +      finally
 +      {
+        if( hasError )
 +              facesContext.responseComplete();
         phases.afterPhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
                  MockLifecycle.INSTANCE));
+       }
      }


it may be cleaner to refactor the phase methods into PhaseExection classes. (remove firing of phase events from the phase methods )

	public class PhaseExecution {
		private PhaseId phaseId;
		
		public PhaseExecution(PhaseId phaseId)
		{
			this.phaseId = phaseId;
		}
		
		protected abstract void execute() throws Exception
		
		public void run() throws Exception {
			fireBefore();
			try
			{
				execute();
			}
			finally
			{
				afterPhase();
			}
		}
		
		protected void fireBefore()
		{
			 phases.beforePhase(new PhaseEvent(facesContext, phaseId,
	                  MockLifecycle.INSTANCE));
		}

		protected void fireAfter()
		{
			 phases.afterPhase(new PhaseEvent(facesContext, phaseId,
	                  MockLifecycle.INSTANCE));
		}
	}
	
	final private PhaseExcecution RESTORE_VIEW_PHASE= new PhaseExecution(PhaseId.RESTORE_VIEW) {
		protected void execute() throws Exception {
			restoreViewPhase();
		}
	};
	
	final private PhaseExcecution RENDER_RESPONSE_PHASE= new PhaseExecution(PhaseId.RENDER_RESPONSE) {
		protected void execute() throws Exception {
			renderResponsePhase();
		}
	};
	...
	

    /**
     * @return true if a response was rendered
     */
    private boolean emulateJsfLifecycle() throws Exception
    {
       RESTORE_VIEW_PHASE.run();
       if ( !isGetRequest() && !skipToRender() )
       {
          APPLY_REQUEST_VALUES_PHASE.run();
          if (!skipToRender())
          {
             PROCESS_VALIDATIONS_PHASE.run();
             if ( !skipToRender() )
             {
                UPDATE_MODEL_VALUES_PHASE.run();
                if ( !skipToRender() )
                {
                   INVOKE_APPLICATION_PHASE.run();
                }
             }
          }
       }
       
       if ( skipRender() )
       {
          // we really should look at redirect parameters here!
          return false;
       }
       else
       {
          RENDER_RESPONSE_PHASE.run();
          return true;
       }
    }



> BaseSeamTest does not properly emulateJsfLifecycle does not handle phase listeners per the jsf 1.2 spec.
> --------------------------------------------------------------------------------------------------------
>
>                 Key: JBSEAM-1822
>                 URL: http://jira.jboss.com/jira/browse/JBSEAM-1822
>             Project: JBoss Seam
>          Issue Type: Bug
>          Components: Test Harness
>    Affects Versions: 2.0.0.BETA1
>            Reporter: Chris Rudd
>
> Under the 1.2 JSF spec, all phase listeners are called with the after phase events reguarless of if an exception was thrown during the phase processing.
> The resulting issue is that when Init.isTransactionMangementEnabled is true, and and an exception (or an AssertionError) is thrown from within the phase method, the SeamPhase listner does not get a chance to handle the condition and rollback the transaction. This leaves the transaction open, and all further tests run for that class are now "tainted" as there is a transaction running that will never be completed.
> Wrapping code in the phase methods (restoreViewPhase,applyRequestValuesPhase,processValidationsPhase,updateModelValuesPhase,invokeApplicationsPhase, renderResponsePhase) like this will resolve the issue :
>       private void renderResponsePhase() throws Exception
>       {
>          phases.beforePhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
>                   MockLifecycle.INSTANCE));
> +        try
> +        {         
>           updateConversationId();
>          
>           renderResponseBegun = true;
>   
>           renderResponse();
>   
>           renderResponseComplete = true;
>   
>           facesContext.getApplication().getStateManager().saveView(facesContext);
>   
>           updateConversationId();
>  +      }
>  +      finally
>  +      {
>          phases.afterPhase(new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE,
>                   MockLifecycle.INSTANCE));
> +       }
>       }
> it may be cleaner to refactor the phase methods into PhaseExection classes. (remove firing of phase events from the phase methods )
> 	public class PhaseExecution {
> 		private PhaseId phaseId;
> 		
> 		public PhaseExecution(PhaseId phaseId)
> 		{
> 			this.phaseId = phaseId;
> 		}
> 		
> 		protected abstract void execute() throws Exception
> 		
> 		public void run() throws Exception {
> 			fireBefore();
> 			try
> 			{
> 				execute();
> 			}
> 			finally
> 			{
> 				afterPhase();
> 			}
> 		}
> 		
> 		protected void fireBefore()
> 		{
> 			 phases.beforePhase(new PhaseEvent(facesContext, phaseId,
> 	                  MockLifecycle.INSTANCE));
> 		}
> 		protected void fireAfter()
> 		{
> 			 phases.afterPhase(new PhaseEvent(facesContext, phaseId,
> 	                  MockLifecycle.INSTANCE));
> 		}
> 	}
> 	
> 	final private PhaseExcecution RESTORE_VIEW_PHASE= new PhaseExecution(PhaseId.RESTORE_VIEW) {
> 		protected void execute() throws Exception {
> 			restoreViewPhase();
> 		}
> 	};
> 	
> 	final private PhaseExcecution RENDER_RESPONSE_PHASE= new PhaseExecution(PhaseId.RENDER_RESPONSE) {
> 		protected void execute() throws Exception {
> 			renderResponsePhase();
> 		}
> 	};
> 	...
> 	
>     /**
>      * @return true if a response was rendered
>      */
>     private boolean emulateJsfLifecycle() throws Exception
>     {
>        RESTORE_VIEW_PHASE.run();
>        if ( !isGetRequest() && !skipToRender() )
>        {
>           APPLY_REQUEST_VALUES_PHASE.run();
>           if (!skipToRender())
>           {
>              PROCESS_VALIDATIONS_PHASE.run();
>              if ( !skipToRender() )
>              {
>                 UPDATE_MODEL_VALUES_PHASE.run();
>                 if ( !skipToRender() )
>                 {
>                    INVOKE_APPLICATION_PHASE.run();
>                 }
>              }
>           }
>        }
>        
>        if ( skipRender() )
>        {
>           // we really should look at redirect parameters here!
>           return false;
>        }
>        else
>        {
>           RENDER_RESPONSE_PHASE.run();
>           return true;
>        }
>     }

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the seam-issues mailing list