org.springframework.faces.webflow.FlowViewStateManager Maven / Gradle / Ivy
/*
* Copyright 2004-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.faces.webflow;
import java.io.IOException;
import javax.faces.application.StateManager;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
/**
* Custom {@link StateManager} that manages the JSF component state in web flow's view scope.
*
* @author Jeremy Grelle
* @author Rossen Stoyanchev
*/
public class FlowViewStateManager extends StateManager {
private static final Log logger = LogFactory.getLog(FlowViewStateManager.class);
protected static final String SERIALIZED_VIEW_STATE = "flowSerializedViewState";
private StateManager delegate;
public FlowViewStateManager(StateManager delegate) {
this.delegate = delegate;
}
protected Object getComponentStateToSave(FacesContext context) {
if (!JsfUtils.isFlowRequest()) {
return super.getComponentStateToSave(context);
}
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot.isTransient()) {
return null;
} else {
return viewRoot.processSaveState(context);
}
}
protected Object getTreeStructureToSave(FacesContext context) {
if (!JsfUtils.isFlowRequest()) {
return super.getTreeStructureToSave(context);
}
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot.isTransient()) {
return null;
} else {
return new TreeStructureManager().buildTreeStructureToSave(viewRoot);
}
}
protected void restoreComponentState(FacesContext context, UIViewRoot viewRoot, String renderKitId) {
if (!JsfUtils.isFlowRequest()) {
super.restoreComponentState(context, viewRoot, renderKitId);
return;
}
RequestContext requestContext = RequestContextHolder.getRequestContext();
FlowSerializedView view = (FlowSerializedView) requestContext.getViewScope().get(SERIALIZED_VIEW_STATE);
viewRoot.processRestoreState(context, view.getComponentState());
logger.debug("UIViewRoot component state restored");
}
protected UIViewRoot restoreTreeStructure(FacesContext context, String viewId, String renderKitId) {
if (!JsfUtils.isFlowRequest()) {
return super.restoreTreeStructure(context, viewId, renderKitId);
}
RequestContext requestContext = RequestContextHolder.getRequestContext();
FlowSerializedView view = (FlowSerializedView) requestContext.getViewScope().get(SERIALIZED_VIEW_STATE);
if (view == null || !view.getViewId().equals(viewId)) {
logger.debug("No matching view in view scope");
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Restoring view root with id '" + viewId + "' from view scope");
}
if (view.getTreeStructure() == null) {
logger.debug("Tree structure is null indicating transient UIViewRoot; returning null");
return null;
}
UIViewRoot viewRoot = new TreeStructureManager().restoreTreeStructure(view.getTreeStructure());
logger.debug("UIViewRoot structure restored");
return viewRoot;
}
public void writeState(FacesContext context, javax.faces.application.StateManager.SerializedView state)
throws IOException {
// Ensures that javax.faces.ViewState hidden field always gets written - needed for third-party component
// compatibility
delegate.writeState(context, state);
}
public void writeState(FacesContext context, Object state) throws IOException {
if (state instanceof Object[]) {
delegate.writeState(context, state); // MyFaces
} else if (state instanceof FlowSerializedView) { // Mojarra
FlowSerializedView view = (FlowSerializedView) state;
delegate.writeState(context, view.asTreeStructAndCompStateArray());
} else {
super.writeState(context, state);
}
}
public boolean isSavingStateInClient(FacesContext context) {
if (!JsfUtils.isFlowRequest()) {
return delegate.isSavingStateInClient(context);
} else {
return false;
}
}
/**
* JSF 1.1 version of state saving
*/
public javax.faces.application.StateManager.SerializedView saveSerializedView(FacesContext context) {
if (context.getViewRoot().isTransient()) {
return null;
}
if (!JsfUtils.isFlowRequest()) {
return delegate.saveSerializedView(context);
}
Object state = saveView(context);
if (state instanceof FlowSerializedView) {
FlowSerializedView serializedState = (FlowSerializedView) state;
return new javax.faces.application.StateManager.SerializedView(serializedState.getTreeStructure(),
serializedState.getComponentState());
} else {
Object[] serializedState = (Object[]) state;
return new javax.faces.application.StateManager.SerializedView(serializedState[0], serializedState[1]);
}
}
/**
*
* JSF 1.2 (or higher) version of state saving.
*
*
* In JSF 2, if partial state saving is enabled this method delegates in order to obtain the serialized view state.
* During rendering JSF calls this method to prepare the state and then calls {@link FlowViewResponseStateManager}
* which writes it to Web Flow's view scope.
*
*
* Nevertheless this method always writes the serialized state to Web Flow's view scope to ensure it is up-to-date
* for cases outside of rendering (e.g. ViewState.updateHistory()) or when the render phase doesn't call
* {@link FlowViewResponseStateManager} such as when processing a partial request.
*/
public Object saveView(FacesContext context) {
if (context.getViewRoot().isTransient()) {
return null;
}
if (!JsfUtils.isFlowRequest()) {
return delegate.saveView(context);
}
FlowSerializedView view = null;
if (JsfRuntimeInformation.isPartialStateSavingSupported()) {
Object[] state = (Object[]) delegate.saveView(context);
view = new FlowSerializedView(context.getViewRoot().getViewId(), state[0], state[1]);
} else {
view = new FlowSerializedView(context.getViewRoot().getViewId(), getTreeStructureToSave(context),
getComponentStateToSave(context));
}
RequestContext requestContext = RequestContextHolder.getRequestContext();
if (logger.isDebugEnabled()) {
logger.debug("Saving view root '" + context.getViewRoot().getViewId() + "' in view scope");
}
requestContext.getViewScope().put(SERIALIZED_VIEW_STATE, view);
return view;
}
/**
*
* In JSF 2 where a partial state saving algorithm is used, this method merely delegates to the next
* ViewStateManager. Thus partial state saving is handled by the JSF 2 runtime. However, a
* {@link FlowViewResponseStateManager} plugged in via {@link FlowRenderKit} will ensure the state is saved in a Web
* Flow view-scoped variable.
*
*/
public UIViewRoot restoreView(FacesContext context, String viewId, String renderKitId) {
if ((!JsfUtils.isFlowRequest()) || JsfRuntimeInformation.isPartialStateSavingSupported()) {
return delegate.restoreView(context, viewId, renderKitId);
} else {
UIViewRoot viewRoot = restoreTreeStructure(context, viewId, renderKitId);
if (viewRoot != null) {
context.setViewRoot(viewRoot);
restoreComponentState(context, viewRoot, renderKitId);
}
return viewRoot;
}
}
@Override
public String getViewState(FacesContext context) {
if (!JsfUtils.isFlowRequest()) {
return delegate.getViewState(context);
}
/*
* Mojarra 2: PartialRequestContextImpl.renderState() invokes this method during Ajax request rendering. We
* overridde it to convert FlowSerializedView state to an array before calling the
* ResponseStateManager.getViewState(), which in turn calls the ServerSideStateHelper and expects state to be an
* array.
*/
Object state = saveView(context);
if (state != null) {
if (state instanceof FlowSerializedView) {
FlowSerializedView view = (FlowSerializedView) state;
state = view.asTreeStructAndCompStateArray();
}
return context.getRenderKit().getResponseStateManager().getViewState(context, state);
}
return null;
}
}