New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restore ViewScope before templates are processed with buildView #787
Comments
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented Finally I found some time to develop a patch for this. It works fine with CS JSF. regards |
@glassfishrobot Commented |
@glassfishrobot Commented This is a reworked patch to restore ViewScope before the templates are processed in buildView(). Until now the full state including ViewScope is restored in UIViewRoot.restoreState(). But this method must only be called after the templates have been processed. To allow restore of ViewScope before processing the templates, I introduced a new method called UIViewRoot.restoreViewScopeState(FacesContext fc, Object state) to restore ViewScope only. In UIViewRoot.restoreState(..) viewScope will only be restored if not done before. I am aware that it seems weird that ViewScope is state saved in the normal UIViewRoot.saveState(..) method and restored with restoreViewScopeState(..). But for saving the state there was no need to change the behaviour unless we want to redesign this in more depth which would involve a pair of method to save/restore ViewScope. We use this solution in CS JSF since about 1/2 year and it worked with partial- and full state saving. regards |
@glassfishrobot Commented Do we need to generalize this or is it sufficient to handle the view scope alone? Arguments in favor of accepting the special case approach directly from 1. The manner in which the pre-restoration-traversal state is obtained 2. An other use-case for pre-restoration-traversal state is dynamic 3. The UIViewRoot is unique among all other components in that you don't Arguments in favor of denying Hanspeter's 20110607 patch in favor of a 1. The additional API required would not be that large. Here is a sketch. Hanspeter, can you please cook up a version of the patch that uses the |
@glassfishrobot Commented |
@glassfishrobot Commented Interesting considerations and I really like more generalized solutions, but I think UIViewRoot is unique for this matter: 1. UIViewRoot is not created from templates and there is also no possibility to do special work in a UIViewRootTagHandler. 2. The state in question is the ViewScope - all other components don't have theyr own scope to restore. 3. PreRestoreStateEvent on the component would be about the same time as PostAddToViewEvent on the component. It's after component was constructed and added to the view but before restore state is processed - restore state is processed after the full component tree was built. 4. In my opinion there is no way to restore some component state before the component is created - and during component creation TagHandler may be used for such action or directly after component construction PostAddToView component event may be used. 5. PreRestoreStateEvent processing would probably cause another tree walk, which some of the EG members will not like. Ed, I don't see the need for that elaboration. Regards |
@glassfishrobot Commented I'm r=edburns for adding this to the trunk. |
@glassfishrobot Commented |
@glassfishrobot Commented Ed |
@glassfishrobot Commented Sending jsf-api/src/main/java/javax/faces/component/UIViewRoot.java (had to reproduce this manually - next time I know) |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented Cheers! Matt |
@glassfishrobot Commented we used that on our custom built JSF RI 2.0 before 2.0.4 without any problem. So you should be save doing the same. regards |
@glassfishrobot Commented The second PostAddToViewEvent has the component in a bad state, with no children. If I refresh the page, everything works as normal. The only way to cause the problem again is to bounce the server, and again on the first page refresh the additional PostAddToViewEvent is fired. Matt |
@glassfishrobot Commented I think it must be related to how the system deals with the templates for the first time, like when it compiles/caches(correct?) them or something its throwing that additional PostAddToViewEvent. I've confirmed that if I remove the code, the additional event goes away as expected. To work around this, I'm serializing my object graph into a hidden input field and restoring on the decode() of my custom composite component (manually grabbing the value from request). This seems to work, and allows us to then dynamically create our component tree during PostAddToView based on the data serialized in that field from request to request (which can change). Thanks for any input on this! Regards, Matt |
@glassfishrobot Commented The changes from this issue only affect restoreState in UIViewRoot and restoreView in StateManagementStrategyImpl - so I can hardly believe it affects the first time a page is rendered. I assume the double delivered PostAddToViewEvent is due to something else. Debug into your PostAddToViewEvent handling will show where the event comes from. Make sure the second one is not from a resource request - which would explain why the component delivered by the event does not have any children or parent. regards |
@glassfishrobot Commented It is strange, that when I revert the changes and re-compile the extra event goes away with the exact same page. As noted above I'm still very new to this technology having come across from ASP.NET, but it does have some similarities and I am working hard to get a better understanding. Thanks, Matt |
@glassfishrobot Commented Test Case: Simple View <h:form prependId="false" id="dynamicForm"> <h:panelGroup id="group"> {testManagedBean.addComponent} "/> </h:form> The action in the managed bean dynamically created an HTMLOutputText component and adds it as a child to the panelGroup ("group"): public void addComponent() { FacesContext ctx = FacesContext.getCurrentInstance(); UIComponent group = ctx.getViewRoot().findComponent("dynamicForm" + UINamingContainer.getSeparatorChar(ctx) + "group"); HtmlOutputText output = new HtmlOutputText(); output.setValue("OUTPUT"); group.getChildren().add(output); } The first button click adds it fine (after the button). The next button click produces the following exception: java.lang.ClassCastException: com.sun.faces.application.view.StateHolderSaver cannot be cast to [Ljava.lang.Object; |
@glassfishrobot Commented |
@glassfishrobot Commented |
@glassfishrobot Commented |
|
Restoring the view with partial state saving enabled, the state is attached to
the component tree after the view-definition is processed (aka the tree is built
from the template). But sometimes the application of the view-definition might
depend on state from the previous request. An example: we have a dialog system
which remembers across requests which dialog is active.
So, to be backwards compatible with JSF 1.2, we need a way to store this state
across requests - it can not be part of the component state, cause the component
state is applied too late.
Our solution - and also recommendation for the next version of the spec - is
that the ViewScope (or the complete state of the ViewRoot component, but
ViewScope is sufficient) is restored before buildView() is called. That would
allow components with dynamic behavior to keep state in the ViewScope.
best regards,
Martin and Hanspeter
=====================
Here what we do currently: we decorated ViewDeclarationLanguage.buildView like
this (ok, for simplification we restored complete ViewRoot state):
public void buildView(FacesContext context, UIViewRoot root) throws
IOException {
if(context.getCurrentPhaseId()== PhaseId.RESTORE_VIEW)
{ //restore the view-scope (part of the UIViewRoot state-saving process) //before we run the templates RenderKit renderKit = context.getRenderKit(); ResponseStateManager rsm = renderKit.getResponseStateManager(); Object[] rawState = (Object[]) rsm.getState(context, root.getViewId()); //noinspection unchecked final Map<String, Object> state = (Map<String,Object>) rawState[1]; Object viewRootState = state.get(root.getClientId()); root.restoreState(context, viewRootState); }
delegate.buildView(context, root);
}
Environment
Operating System: All
Platform: All
Affected Versions
[2.0]
The text was updated successfully, but these errors were encountered: