[
http://jira.jboss.com/jira/browse/JBSEAM-1822?page=comments#action_12378718 ]
Sam Roberton commented on JBSEAM-1822:
--------------------------------------
Turns out Jira won't let me re-open this issue, nor attach files for a test case, so
Shane, I'm emailing you the test case. Could you re-open this and attach the file,
please?
The test is very simple. There's a single component, StringHolder, which is
conversation-scoped. The test class makes three requests: (1) a non-faces request which
causes the component to be created by retrieving the value which is held; (2) a faces
request which updates the value held by the component, but which throws an exception in
invokeApplication(); and (3) a faces request which retrieves the value held by the
component again.
The third request causes a new StringHolder to be created, even though we're still in
the same conversation. I believe this is because the ServerConversationContext.flush()
method is not called for the second request, since that method is invoked (indirectly) by
SeamPhaseListener.afterRender(FacesContext).
Interestingly, even though a new StringHolder is created, the value retrieved from the
StringHolder by the third request is the value second by the second request. No idea how
that works...
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
Assigned To: Shane Bryzak
Fix For: 2.0.0.GA
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