Announcement Announcement Module
No announcement yet.
SWF + JSF2 + View restoration across redirect problem Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • SWF + JSF2 + View restoration across redirect problem

    I was struggling for a while trying to figure out why, when standard JSF validation (i.e. required="true") failed after a form submission, none of the values entered were present in the form after submission - just empty components along with the expected error messages. I debugged through the SWF code and found the following comment which I think explains everything...

    // In JSF 2 the partial state saving algorithm attaches a system event listener to the UIViewRoot which
    // holds on to a reference to the FacesContext instance. The FacesContext instance is released at end of
    // each request. Hence, keeping the UIViewRoot across the redirect is not feasible.
    // @see com.sun.faces.context.StateContext$AddRemoveListener
    logger.debug("User event state requested but not saved.");
    return null;
    Please correct me if I am wrong but I think this means that the scenario I mentioned will never work. This is a pretty big problem for me. I can't envisage any UI in which the user has to reinput all data in a form because of one validation failure. I think users will become pretty annoyed with a UI like that.

    Is there a way around this problem?

    If there is I assume it will involve breaking the Submit -> Redirect -> Get pattern which again is pretty undesirable.

  • #2
    I've spent some time thinking about this problem and *think* I've found a solution....

    There is already a state saving mechanism in JSF which is used to persist views across requests so couldn't this be used?

    I tried a few things like using the configured StateManager for the application but I couldn't get anything to work. The main reason for this was that the StateManager implementations use the ResponseStateManager class for restoring the state. In this instance we don't want our state stored by this class - we want to put it into flash scope.

    I then discovered that UIViewRoot has two methods processSaveState and processRestoreState, which seem to save and restore the state for the entire component tree so I tried using these methods and they worked straight off!!! I'm not sure if this is the right thing to do or not but it definitely works.

    Here's what I did...

    I create a new Jsf2ViewRootHolder class

     * Holder for the JSF2 UIViewRoot state
     * @author Gareth Webbley
    class Jsf2ViewRootHolder implements Serializable {
    	private String viewId;
    	private Object viewState;
    	public Jsf2ViewRootHolder(UIViewRoot viewRoot) {
    		this.viewId = viewRoot.getViewId();
    		FacesContext context = FacesContext.getCurrentInstance();
    		this.viewState = viewRoot.processSaveState(context);
    	public String getViewId() {
    		return viewId;
    	public void setViewId(String viewId) {
    		this.viewId = viewId;
    	public UIViewRoot getViewRoot(UIViewRoot viewRoot) {
    		FacesContext context = FacesContext.getCurrentInstance();
    		if (viewRoot != null && viewState != null) {
    			viewRoot.processRestoreState(context, viewState);
    		return viewRoot;
    Then I modified the getUserEventState method of JsfView to save the state...

    	public Serializable getUserEventState() {
    		if (isLessThanJsf20()) {
    			// Set the temporary UIViewRoot state so that it will be available across the redirect
    			return new ViewRootHolder(getViewRoot());
    		} else {
    			// Set the temporary UIViewRoot state so that it will be available across the redirect
    			return new Jsf2ViewRootHolder(getViewRoot());
    Finally I modified the restoreFlowView method in FlowViewHandler to restore the previously saved state after resuming the flow....

    	private UIViewRoot restoreFlowView(FacesContext facesContext, String resourcePath) {
    		RequestContext context = RequestContextHolder.getRequestContext();
    		UIViewRoot result = null;
    		Object state = context.getFlashScope().get(View.USER_EVENT_STATE_ATTRIBUTE);
    		if (state != null && state instanceof ViewRootHolder) {
    			ViewRootHolder holder = (ViewRootHolder) state;
    			if (holder.getViewRoot() != null && holder.getViewRoot().getViewId().equals(resourcePath)) {
    				result = holder.getViewRoot();
    		if (result == null) {
    			result = delegate.restoreView(facesContext, resourcePath);
    		if (state != null && state instanceof Jsf2ViewRootHolder
    				&& ((Jsf2ViewRootHolder) state).getViewId().equals(resourcePath)) {
    			result = ((Jsf2ViewRootHolder) state).getViewRoot(result);
    		return result;
    I'm not sure what the SWF gurus think of this solution i.e. whether it's the right solution or not but hopefully it's a starting point for a fix to this problem.

    I'd be grateful if someone could let me know what they think.



    • #3
      SWF + JSF2 + View restoration across redirect problem

      Hi gareth:

      I have similar problem: using <f:validateBean/> instead jsf tags (required for example) doesn't work properly. Restoring UI cannot restore the validator -> cannot make validation (UIInput has validators field to null, so processValidators cannot validate). Any ideas????

      NOTE: calling saveView manually (saveState on JsfView only call saveView if it is an ajax request, so I override render on FlowLifecycle), this validator are not saved, it is transient!!!!!.



      • #4
        Redirect in the same state


        you can always change webflow behaviour by setting redirect in the same view state to false. Also you can set redirect="false" on your view-state definiction in webflow.

        Read this