com.sun.faces.application.StateManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.faces.application.StateManagerImpl.java

Source

/* 
 * $Id: StateManagerImpl.java,v 1.24.12.2.2.1 2006/04/12 19:32:03 ofung Exp $ 
 */

/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at
 * https://javaserverfaces.dev.java.net/CDDL.html or
 * legal/CDDLv1.0.txt. 
 * See the License for the specific language governing
 * permission and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at legal/CDDLv1.0.txt.    
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * [Name of File] [ver.__] [Date]
 * 
 * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
 */

// StateManagerImpl.java 

package com.sun.faces.application;

import com.sun.faces.RIConstants;
import com.sun.faces.util.TreeStructure;
import com.sun.faces.util.Util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.collections.LRUMap;

import javax.faces.application.StateManager;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.ResponseStateManager;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.faces.component.NamingContainer;

/**
 * <B>StateManagerImpl</B> is the default implementation class for
 * StateManager.
 *
 * @version $Id: StateManagerImpl.java,v 1.24.12.2.2.1 2006/04/12 19:32:03 ofung Exp $
 * @see javax.faces.application.ViewHandler
 */
public class StateManagerImpl extends StateManager {

    private static final Log log = LogFactory.getLog(StateManagerImpl.class);
    private static final String NUMBER_OF_VIEWS_IN_SESSION = RIConstants.FACES_PREFIX
            + "NUMBER_OF_VIEWS_IN_SESSION";
    private static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 15;

    private static final String NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION = RIConstants.FACES_PREFIX
            + "NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION";
    private static final int DEFAULT_NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION = 15;

    /**
     * Number of views in logical view to be saved in session.
     */
    int noOfViews = 0;
    int noOfViewsInLogicalView = 0;

    public SerializedView saveSerializedView(FacesContext context) throws IllegalStateException {
        SerializedView result = null;
        Object treeStructure = null;
        Object componentState = null;
        // irrespective of method to save the tree, if the root is transient
        // no state information needs to  be persisted.
        UIViewRoot viewRoot = context.getViewRoot();
        if (viewRoot.isTransient()) {
            return result;
        }

        // honor the requirement to check for id uniqueness
        checkIdUniqueness(context, viewRoot, new HashSet());

        if (log.isDebugEnabled()) {
            log.debug("Begin creating serialized view for " + viewRoot.getViewId());
        }
        result = new SerializedView(treeStructure = getTreeStructureToSave(context),
                componentState = getComponentStateToSave(context));
        Util.doAssert(treeStructure instanceof Serializable);
        Util.doAssert(componentState instanceof Serializable);

        if (log.isDebugEnabled()) {
            log.debug("End creating serialized view " + viewRoot.getViewId());
        }
        if (!isSavingStateInClient(context)) {
            //
            // Server Side state saving is handled stored in two nested LRU maps
            // in the session.
            //
            // The first map is called the LOGICAL_VIEW_MAP.  A logical view
            // is a top level view that may have one or more actual views inside
            // of it.  This will be the case when you have a frameset, or an
            // application that has multiple windows operating at the same time.
            // The LOGICAL_VIEW_MAP map contains 
            // an entry for each logical view, up to the limit specified by the
            // numberOfViewsParameter.  Each entry in the LOGICAL_VIEW_MAP
            // is an LRU Map, configured with the numberOfViewsInLogicalView
            // parameter.  
            //
            // The motivation for this is to allow better memory tuning for 
            // apps that need this multi-window behavior.

            String id = null, idInActualMap = null, idInLogicalMap = (String) context.getExternalContext()
                    .getRequestMap().get(RIConstants.LOGICAL_VIEW_MAP);
            LRUMap logicalMap = null, actualMap = null;
            int logicalMapSize = getNumberOfViewsParameter(context),
                    actualMapSize = getNumberOfViewsInLogicalViewParameter(context);

            Object stateArray[] = { treeStructure, componentState };
            Map sessionMap = Util.getSessionMap(context);

            synchronized (this) {
                if (null == (logicalMap = (LRUMap) sessionMap.get(RIConstants.LOGICAL_VIEW_MAP))) {
                    logicalMap = new LRUMap(logicalMapSize);
                    sessionMap.put(RIConstants.LOGICAL_VIEW_MAP, logicalMap);
                }
                Util.doAssert(null != logicalMap);

                if (null == idInLogicalMap) {
                    idInLogicalMap = createUniqueRequestId();
                }
                Util.doAssert(null != idInLogicalMap);

                idInActualMap = createUniqueRequestId();
                if (null == (actualMap = (LRUMap) logicalMap.get(idInLogicalMap))) {
                    actualMap = new LRUMap(actualMapSize);
                    logicalMap.put(idInLogicalMap, actualMap);
                }
                id = idInLogicalMap + NamingContainer.SEPARATOR_CHAR + idInActualMap;
                result = new SerializedView(id, null);
                actualMap.put(idInActualMap, stateArray);
            }
        }

        return result;
    }

    char requestIdSerial = 0;

    private String createUniqueRequestId() {
        if (requestIdSerial++ == Character.MAX_VALUE) {
            requestIdSerial = 0;
        }
        return UIViewRoot.UNIQUE_ID_PREFIX + ((int) requestIdSerial);
    }

    protected void checkIdUniqueness(FacesContext context, UIComponent component, Set componentIds)
            throws IllegalStateException {
        UIComponent kid;
        // deal with children that are marked transient.
        Iterator kids = component.getChildren().iterator();
        String id;
        String messageString;
        while (kids.hasNext()) {
            kid = (UIComponent) kids.next();
            // check for id uniqueness
            id = kid.getClientId(context);
            if (id != null && !componentIds.add(id)) {
                messageString = Util.getExceptionMessageString(Util.DUPLICATE_COMPONENT_ID_ERROR_ID,
                        new Object[] { id });
                if (log.isErrorEnabled()) {
                    log.error(messageString);
                }
                throw new IllegalStateException(messageString);
            }

            checkIdUniqueness(context, kid, componentIds);
        }
        // deal with facets that are marked transient.
        kids = component.getFacets().values().iterator();
        while (kids.hasNext()) {
            kid = (UIComponent) kids.next();
            // check for id uniqueness
            id = kid.getClientId(context);
            if (id != null && !componentIds.add(id)) {
                messageString = Util.getExceptionMessageString(Util.DUPLICATE_COMPONENT_ID_ERROR_ID,
                        new Object[] { id });
                if (log.isErrorEnabled()) {
                    log.error(messageString);
                }
                throw new IllegalStateException(messageString);
            }

            checkIdUniqueness(context, kid, componentIds);

        }
    }

    protected Object getComponentStateToSave(FacesContext context) {
        return context.getViewRoot().processSaveState(context);
    }

    protected Object getTreeStructureToSave(FacesContext context) {
        TreeStructure structRoot = null;
        UIComponent viewRoot = context.getViewRoot();
        if (!(viewRoot.isTransient())) {
            structRoot = new TreeStructure(viewRoot);
            buildTreeStructureToSave(context, viewRoot, structRoot, null);
        }
        return structRoot;
    }

    public UIViewRoot restoreView(FacesContext context, String viewId, String renderKitId) {
        if (null == renderKitId) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " renderKitId " + renderKitId;
            throw new IllegalArgumentException(message);
        }

        UIViewRoot viewRoot = null;
        if (isSavingStateInClient(context)) {
            // restore view from response.
            if (log.isDebugEnabled()) {
                log.debug("Begin restoring view from response " + viewId);
            }
            viewRoot = restoreTreeStructure(context, viewId, renderKitId);
            if (viewRoot != null) {
                restoreComponentState(context, viewRoot, renderKitId);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Possibly a new request. Tree structure could not " + " be restored for " + viewId);
                }
            }
            if (log.isDebugEnabled()) {
                log.debug("End restoring view from response " + viewId);
            }
        } else {
            // restore tree from session.
            // The ResponseStateManager implementation may be using the new methods or
            // deprecated methods.  We need to know which one to call.
            Object id = null;
            ResponseStateManager rsm = Util.getResponseStateManager(context, renderKitId);
            id = rsm.getTreeStructureToRestore(context, viewId);

            if (null != id) {
                if (log.isDebugEnabled()) {
                    log.debug("Begin restoring view in session for viewId " + viewId);
                }
                String idString = (String) id, idInLogicalMap = null, idInActualMap = null;

                int sep = idString.indexOf(NamingContainer.SEPARATOR_CHAR);
                Util.doAssert(-1 != sep);
                Util.doAssert(sep < idString.length());

                idInLogicalMap = idString.substring(0, sep);
                idInActualMap = idString.substring(sep + 1);

                ExternalContext externalCtx = context.getExternalContext();
                Object sessionObj = externalCtx.getSession(false);

                // stop evaluating if the session is not available
                if (sessionObj == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Can't Restore Server View State, session expired for viewId: " + viewId);
                    }
                    return null;
                }

                Map logicalMap = null, actualMap = null, sessionMap = externalCtx.getSessionMap();

                TreeStructure structRoot = null;
                Object[] stateArray = null;
                synchronized (sessionObj) {
                    logicalMap = (Map) sessionMap.get(RIConstants.LOGICAL_VIEW_MAP);
                    if (logicalMap != null) {
                        actualMap = (Map) logicalMap.get(idInLogicalMap);
                        if (actualMap != null) {
                            context.getExternalContext().getRequestMap().put(RIConstants.LOGICAL_VIEW_MAP,
                                    idInLogicalMap);
                            stateArray = (Object[]) actualMap.get(idInActualMap);
                        }
                    }
                }
                if (stateArray == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Session Available, but View State does not exist for viewId: " + viewId);
                    }
                    return null;
                }
                structRoot = (TreeStructure) stateArray[0];
                viewRoot = (UIViewRoot) structRoot.createComponent();
                restoreComponentTreeStructure(structRoot, viewRoot);

                viewRoot.processRestoreState(context, stateArray[1]);

                if (log.isDebugEnabled()) {
                    log.debug("End restoring view in session for viewId " + viewId);
                }
            }
        }
        return viewRoot;
    }

    protected void restoreComponentState(FacesContext context, UIViewRoot root, String renderKitId) {
        if (null == renderKitId) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " renderKitId " + renderKitId;
            throw new IllegalArgumentException(message);
        }
        Object state = null;
        ResponseStateManager rsm = Util.getResponseStateManager(context, renderKitId);
        state = rsm.getComponentStateToRestore(context);
        root.processRestoreState(context, state);
    }

    protected UIViewRoot restoreTreeStructure(FacesContext context, String viewId, String renderKitId) {
        if (null == renderKitId) {
            String message = Util.getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID);
            message = message + " renderKitId " + renderKitId;
            throw new IllegalArgumentException(message);
        }
        UIComponent viewRoot = null;
        TreeStructure structRoot = null;
        ResponseStateManager rsm = Util.getResponseStateManager(context, renderKitId);
        structRoot = (TreeStructure) rsm.getTreeStructureToRestore(context, viewId);
        if (structRoot == null) {
            return null;
        }
        viewRoot = structRoot.createComponent();
        restoreComponentTreeStructure(structRoot, viewRoot);
        return ((UIViewRoot) viewRoot);
    }

    public void writeState(FacesContext context, SerializedView state) throws IOException {
        String renderKitId = context.getViewRoot().getRenderKitId();
        ResponseStateManager rsm = Util.getResponseStateManager(context, renderKitId);
        rsm.writeState(context, state);
    }

    /**
     * Builds a hierarchy of TreeStrucure objects simulating the component
     * tree hierarchy.
     */
    public void buildTreeStructureToSave(FacesContext context, UIComponent component, TreeStructure treeStructure,
            Set componentIds) {
        // traverse the component hierarchy and save the tree structure 
        // information for every component.

        // Set for catching duplicate IDs
        if (null == componentIds) {
            componentIds = new HashSet();
        }

        // save the structure info of the children of the component 
        // being processed.
        Iterator kids = component.getChildren().iterator();
        String id;
        String messageString = null;
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();

            // check for id uniqueness
            id = kid.getClientId(context);
            if (id != null && !componentIds.add(id)) {
                messageString = Util.getExceptionMessageString(Util.DUPLICATE_COMPONENT_ID_ERROR_ID,
                        new Object[] { id });
                if (log.isErrorEnabled()) {
                    log.error(messageString);
                }
                throw new IllegalStateException(messageString);
            }

            // if a component is marked transient do not persist its state as
            // well as its children.
            if (!kid.isTransient()) {
                TreeStructure treeStructureChild = new TreeStructure(kid);
                treeStructure.addChild(treeStructureChild);
                buildTreeStructureToSave(context, kid, treeStructureChild, componentIds);
            }
        }

        // save structure info of the facets of the component currenly being 
        // processed.
        Iterator facets = component.getFacets().keySet().iterator();
        while (facets.hasNext()) {
            String facetName = (String) facets.next();
            UIComponent facetComponent = (UIComponent) component.getFacets().get(facetName);

            // check for id uniqueness
            id = facetComponent.getClientId(context);
            if (id != null && !componentIds.add(id)) {
                messageString = Util.getExceptionMessageString(Util.DUPLICATE_COMPONENT_ID_ERROR_ID,
                        new Object[] { id });
                if (log.isErrorEnabled()) {
                    log.error(messageString);
                }
                throw new IllegalStateException(messageString);
            }

            // if a facet is marked transient do not persist its state as well as
            // its children.
            if (!(facetComponent.isTransient())) {
                TreeStructure treeStructureFacet = new TreeStructure(facetComponent);
                treeStructure.addFacet(facetName, treeStructureFacet);
                // process children of facet.
                buildTreeStructureToSave(context, facetComponent, treeStructureFacet, componentIds);
            }
        }
    }

    /**
     * Reconstitutes the component tree from TreeStructure hierarchy
     */
    public void restoreComponentTreeStructure(TreeStructure treeStructure, UIComponent component) {
        // traverse the tree strucure hierarchy and restore component
        // structure.

        // restore the structure of the children of the component being processed.
        Iterator kids = treeStructure.getChildren();
        while (kids.hasNext()) {
            TreeStructure kid = (TreeStructure) kids.next();
            UIComponent child = kid.createComponent();
            component.getChildren().add(child);
            restoreComponentTreeStructure(kid, child);
        }

        // process facets
        Iterator facets = treeStructure.getFacetNames();
        while (facets.hasNext()) {
            String facetName = (String) facets.next();
            TreeStructure facetTreeStructure = treeStructure.getTreeStructureForFacet(facetName);
            UIComponent facetComponent = facetTreeStructure.createComponent();
            component.getFacets().put(facetName, facetComponent);
            restoreComponentTreeStructure(facetTreeStructure, facetComponent);
        }
    }

    /**
     * Returns the <code> UIViewRoot</code> corresponding the 
     * <code> viewId </code> by restoring the view structure and state.
     */
    protected UIViewRoot restoreSerializedView(FacesContext context, SerializedView sv, String viewId) {
        if (sv == null) {
            if (log.isDebugEnabled()) {
                log.debug("Possibly a new request. Tree structure could not " + " be restored for " + viewId);
            }
            return null;
        }
        TreeStructure structRoot = (TreeStructure) sv.getStructure();
        if (structRoot == null) {
            return null;
        }
        UIComponent viewRoot = structRoot.createComponent();
        if (viewRoot != null) {
            restoreComponentTreeStructure(structRoot, viewRoot);
            Object state = sv.getState();
            viewRoot.processRestoreState(context, state);
        }
        return ((UIViewRoot) viewRoot);
    }

    /**
     * Returns the value of ServletContextInitParameter that specifies the
     * maximum number of logical views to be saved in session. If none is specified
     * returns <code>DEFAULT_NUMBER_OF_VIEWS_IN_SESSION</code>.
     */
    protected int getNumberOfViewsParameter(FacesContext context) {
        if (noOfViews != 0) {
            return noOfViews;
        }
        noOfViews = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
        String noOfViewsStr = context.getExternalContext().getInitParameter(NUMBER_OF_VIEWS_IN_SESSION);
        if (noOfViewsStr != null) {
            try {
                noOfViews = Integer.valueOf(noOfViewsStr).intValue();
            } catch (NumberFormatException nfe) {
                if (log.isDebugEnabled()) {
                    log.debug("Error parsing the servetInitParameter " + NUMBER_OF_VIEWS_IN_SESSION
                            + ". Using default " + noOfViews);
                }
            }
        }
        return noOfViews;
    }

    /**
     * Returns the value of ServletContextInitParameter that specifies the
     * maximum number of views to be saved in this logical view. If none is specified
     * returns <code>DEFAULT_NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION</code>.
     */
    protected int getNumberOfViewsInLogicalViewParameter(FacesContext context) {
        if (noOfViewsInLogicalView != 0) {
            return noOfViewsInLogicalView;
        }
        noOfViewsInLogicalView = DEFAULT_NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION;
        String noOfViewsStr = context.getExternalContext()
                .getInitParameter(NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION);
        if (noOfViewsStr != null) {
            try {
                noOfViewsInLogicalView = Integer.valueOf(noOfViewsStr).intValue();
            } catch (NumberFormatException nfe) {
                if (log.isDebugEnabled()) {
                    log.debug("Error parsing the servetInitParameter " + NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION
                            + ". Using default " + noOfViewsInLogicalView);
                }
            }
        }
        return noOfViewsInLogicalView;
    }

}