Bug in Seams Page Context?
noxhoej Jan 7, 2011 7:12 AMHi
I seem to have run into a bug in the way Seams Page context works. Or at least a very bug-like behavior.
It seems that if a page scoped Seam Component is not required until the IncokeApplication phase, but is then needed several times during this phase, a new instance is created every time, and only the last one survives in the page context!?!?
To illustrate this behavior with a reasonable small amount of code, I have had to create the very contrived example below - but it illustrates the behavior.
If you
- Load a fresh test.xhtml in your browser
- Press the
Update Controller Value without rules check
button - Press the
Assign Controller Value to Model
you will see that the model value correctly ends up being New controller value
- just as I would expect.
If instead, you
- Load a fresh test.xhtml in your browser
- Press the
Update Controller Value with rules check
button - Press the
Assign Controller Value to Model
you will see that the model value now ends up being Initial controller value
.
In both cases, the testController
Seam Component, is not created until the Invoke Application Phase, when you click the Update Controller Value ...
button in step 2.
But in the second case, the component is created twice!
- First, a new testController is created (and stored in the page contest) during the resolving of the
target
attribute of the setPropertyActionListener. Then the setPropertyActionListener sets theNew controller value
on the testController. - Second, when the
action
attribute of the <h:commandButton> is resolved, the testController is needed again - and the page context doesn't find the previously created instance. So it creates a new instance - which of course contains theInitial controller value
, and proceeds to store it in the page context overwriting the already created instance.
The reason that the page context doesn't find the testController the second time around, is actually documented in the PageContext javadoc:
During the INVOKE_APPLICATION phase, set() and remove() manipulate the context of the page that is about to be rendered, while get() returns values from the page that was the source of the request.
but I can't believe, that the resulting behavior is what was intended?
Maybe get() needs to look at not only the old page context, but the newly created entries as well...?
Regards,
Nicholas Oxhøj
TestModel.java:
@Name("testModel") @Scope(ScopeType.PAGE) @AutoCreate public class TestModel { private String value = "Initial model value"; public String getValue() { return value; } public void setValue( String aValue ) { value = aValue; } }
TestController.java:
@Name("testController") @Scope(ScopeType.PAGE) public class TestController { private String valueToAssign = "Initial controller value"; @In private TestModel testModel; public void checkRules() { // Check some business rules or something... } public String getValueToAssign() { return valueToAssign; } public void setValueToAssign( String aValueToAssign ) { valueToAssign = aValueToAssign; } }
test.xhtml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xml:lang="en" lang="en"> <body> <h:form> <h:commandButton value="Update Controller Value without rules check"> <f:setPropertyActionListener value="New controller value" target="#{testController.valueToAssign}"/> </h:commandButton> <h:commandButton value="Update Controller Value with rules check" action="#{testController.checkRules}"> <f:setPropertyActionListener value="New controller value" target="#{testController.valueToAssign}"/> </h:commandButton> <br/> <h:commandButton value="Assign Controller Value to Model"> <f:setPropertyActionListener value="#{testController.valueToAssign}" target="#{testModel.value}"/> </h:commandButton> <br/> Model Value: <h:outputText value="#{testModel.value}"/> </h:form> </body> </html>