Package org.apache.myfaces.application.jsp

Source Code of org.apache.myfaces.application.jsp.JspStateManagerImpl$SerializedViewKey

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.myfaces.application.jsp;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.application.MyfacesStateManager;
import org.apache.myfaces.application.TreeStructureManager;
import org.apache.myfaces.renderkit.MyfacesResponseStateManager;
import org.apache.myfaces.shared_impl.renderkit.ViewSequenceUtils;
import org.apache.myfaces.shared_impl.util.MyFacesObjectInputStream;

import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import java.io.*;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
* Default StateManager implementation for use when views are defined
* via tags in JSP pages.
*
* @author Thomas Spiegl (latest modification by $Author: lu4242 $)
* @author Manfred Geiler
* @version $Revision: 694453 $ $Date: 2008-09-11 15:12:45 -0500 (Thu, 11 Sep 2008) $
*/
public class JspStateManagerImpl
    extends MyfacesStateManager {
    private static final Log log = LogFactory.getLog(JspStateManagerImpl.class);
    private static final String SERIALIZED_VIEW_SESSION_ATTR
        = JspStateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
    private static final String SERIALIZED_VIEW_REQUEST_ATTR
        = JspStateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
    private static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR
        = JspStateManagerImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";

    /**
     * Only applicable if state saving method is "server" (= default).
     * Defines the amount (default = 20) of the latest views are stored in session.
     */
    /**
     * Only applicable if state saving method is "server" (= default).
     * Defines the amount (default = 20) of the latest views are stored in session.
     */
    private static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";

    /**
     * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
     */
    /**
     * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
     */
    private static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;

    /**
     * Only applicable if state saving method is "server" (= default).
     * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
     * If <code>false</code> the state will not be serialized to a byte stream.
     */
    private static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";

    /**
     * Only applicable if state saving method is "server" (= default) and if <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
     * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
     * If <code>false</code> the state will not be compressed.
     */
    private static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";

    /**
     * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
     */
    private static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;

    /**
     * Default value for <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
     */
    private static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;

    /**
     * Define the way of handle old view references(views removed from session), making possible to
     * store it in a cache, so the state manager first try to get the view from the session. If is it
     * not found and soft or weak ReferenceMap is used, it try to get from it.
     * <p>
     * Only applicable if state saving method is "server" (= default).
     * </p>
     * <p>
     * The gc is responsible for remove the views, according to the rules used for soft, weak or phantom
     * references. If a key in soft and weak mode is garbage collected, its values are purged.
     * </p>
     * <p>
     * By default no cache is used, so views removed from session became phantom references.
     * </p>
     * <ul>
     * <li> off, no: default, no cache is used</li>
     * <li> hard-soft: use an ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT)</li>
     * <li> soft: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true) </li>
     * <li> soft-weak: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true) </li>
     * <li> weak: use an ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true) </li>
     * </ul>
     *
     */
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE = "org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE";
   
    /**
     * This option uses an hard-soft ReferenceMap, but it could cause a
     * memory leak, because the keys are not removed by any method
     * (MYFACES-1660). So use with caution.
     */
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT = "hard-soft";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT = "soft";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK = "soft-weak";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK = "weak";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF = "off";

    private static final int UNCOMPRESSED_FLAG = 0;
    private static final int COMPRESSED_FLAG = 1;

    private RenderKitFactory _renderKitFactory = null;

    public JspStateManagerImpl() {
        if (log.isTraceEnabled()) log.trace("New JspStateManagerImpl instance created");
    }

    protected Object getComponentStateToSave(FacesContext facesContext) {
        if (log.isTraceEnabled()) log.trace("Entering getComponentStateToSave");

        UIViewRoot viewRoot = facesContext.getViewRoot();
        if (viewRoot.isTransient()) {
            return null;
        }

        Object serializedComponentStates = viewRoot.processSaveState(facesContext);
        //Locale is a state attribute of UIViewRoot and need not be saved explicitly
        if (log.isTraceEnabled()) log.trace("Exiting getComponentStateToSave");
        return serializedComponentStates;
    }

    /**
     * Return an object which contains info about the UIComponent type
     * of each node in the view tree. This allows an identical UIComponent
     * tree to be recreated later, though all the components will have
     * just default values for their members.
     */
    protected Object getTreeStructureToSave(FacesContext facesContext) {
        if (log.isTraceEnabled()) log.trace("Entering getTreeStructureToSave");
        UIViewRoot viewRoot = facesContext.getViewRoot();
        if (viewRoot.isTransient()) {
            return null;
        }
        TreeStructureManager tsm = new TreeStructureManager();
        Object retVal = tsm.buildTreeStructureToSave(viewRoot);
        if (log.isTraceEnabled()) log.trace("Exiting getTreeStructureToSave");
        return retVal;
    }

    /**
     * Given a tree of UIComponent objects created the default constructor
     * for each node, retrieve saved state info (from either the client or
     * the server) and walk the tree restoring the members of each node
     * from the saved state information.
     */
    protected void restoreComponentState(FacesContext facesContext,
                                         UIViewRoot uiViewRoot,
                                         String renderKitId) {
        if (log.isTraceEnabled()) log.trace("Entering restoreComponentState");

        //===========================================
        // first, locate the saved state information
        //===========================================

        Object serializedComponentStates;
        if (isSavingStateInClient(facesContext)) {
            RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
            ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
            serializedComponentStates = responseStateManager.getComponentStateToRestore(facesContext);
            if (serializedComponentStates == null) {
                log.error("No serialized component state found in client request!");
                // mark UIViewRoot invalid by resetting view id
                uiViewRoot.setViewId(null);
                return;
            }
        }
        else {
            String viewId = uiViewRoot.getViewId();
            String sequenceStr = getSequenceString(facesContext, renderKitId, viewId);
            SerializedView serializedView = getSerializedViewFromServletSession(facesContext,
                                                                                viewId,
                                                                                sequenceStr);
            if (serializedView == null) {
                log.error("No serialized view found in server session!");
                // mark UIViewRoot invalid by resetting view id
                uiViewRoot.setViewId(null);
                return;
            }
            serializedComponentStates = serializedView.getState();
            if (serializedComponentStates == null) {
                log.error("No serialized component state found in server session!");
                return;
            }
        }

        if (uiViewRoot.getRenderKitId() == null) {
            //Just to be sure...
            uiViewRoot.setRenderKitId(renderKitId);
        }

        // now ask the view root component to restore its state
        uiViewRoot.processRestoreState(facesContext, serializedComponentStates);

        if (log.isTraceEnabled()) log.trace("Exiting restoreComponentState");
    }

    /**
     * See getTreeStructureToSave.
     */
    protected UIViewRoot restoreTreeStructure(FacesContext facesContext,
                                              String viewId,
                                              String renderKitId) {
        if (log.isTraceEnabled()) log.trace("Entering restoreTreeStructure");

        UIViewRoot uiViewRoot;
        if (isSavingStateInClient(facesContext)) {
            //reconstruct tree structure from request
            RenderKit rk = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
            ResponseStateManager responseStateManager = rk.getResponseStateManager();
            Object treeStructure = responseStateManager.getTreeStructureToRestore(facesContext, viewId);
            if (treeStructure == null) {
                if (log.isDebugEnabled())
                    log.debug("Exiting restoreTreeStructure - No tree structure state found in client request");
                return null;
            }

            TreeStructureManager tsm = new TreeStructureManager();
            uiViewRoot = tsm.restoreTreeStructure((TreeStructureManager.TreeStructComponent) treeStructure);
            if (log.isTraceEnabled()) log.trace("Tree structure restored from client request");
        }
        else {
            String sequenceStr = getSequenceString(facesContext, renderKitId, viewId);
            //reconstruct tree structure from ServletSession
            SerializedView serializedView = getSerializedViewFromServletSession(facesContext,
                                                                                viewId,
                                                                                sequenceStr);
            if (serializedView == null) {
                if (log.isDebugEnabled())
                    log.debug("Exiting restoreTreeStructure - No serialized view found in server session!");
                return null;
            }

            Object treeStructure = serializedView.getStructure();
            if (treeStructure == null) {
                if (log.isDebugEnabled())
                    log.debug("Exiting restoreTreeStructure - No tree structure state found in server session, former UIViewRoot must have been transient");
                return null;
            }

            TreeStructureManager tsm = new TreeStructureManager();
            uiViewRoot = tsm.restoreTreeStructure((TreeStructureManager.TreeStructComponent) serializedView.getStructure());
            if (log.isTraceEnabled()) log.trace("Tree structure restored from server session");
        }

        if (log.isTraceEnabled()) log.trace("Exiting restoreTreeStructure");
        return uiViewRoot;
    }

    private String getSequenceString(FacesContext facesContext, String renderKitId, String viewId) {
        RenderKit rk = getRenderKitFactory().getRenderKit(facesContext, renderKitId);

        if(rk==null) //first access of the renderkit in a typical application - at this point, renderkit needs to be available. if it isn't we'll throw an exception
            throw new NullPointerException("RenderKit for renderKitId : "+renderKitId + " on viewId : " + viewId + " could not be retrieved. Please specify a valid renderkit-id.");

        ResponseStateManager responseStateManager = rk.getResponseStateManager();
        String sequenceStr = (String) responseStateManager.getTreeStructureToRestore(facesContext, viewId);
        return sequenceStr;
    }

    public UIViewRoot restoreView(FacesContext facescontext, String viewId, String renderKitId) {
        if (log.isTraceEnabled()) log.trace("Entering restoreView");

        UIViewRoot uiViewRoot = restoreTreeStructure(facescontext, viewId, renderKitId);
        if (uiViewRoot != null) {
            uiViewRoot.setViewId(viewId);
            restoreComponentState(facescontext, uiViewRoot, renderKitId);
            String restoredViewId = uiViewRoot.getViewId();
            if (restoredViewId == null || !(restoredViewId.equals(viewId))) {
                if (log.isTraceEnabled()) log.trace("Exiting restoreView - restored view is null.");
                return null;
            }
        }

        if (log.isTraceEnabled()) log.trace("Exiting restoreView");

        return uiViewRoot;
    }

    public SerializedView saveSerializedView(FacesContext facesContext) throws IllegalStateException {
        if (log.isTraceEnabled()) log.trace("Entering saveSerializedView");

        checkForDuplicateIds(facesContext, facesContext.getViewRoot(), new HashSet());

        if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - Checked for duplicate Ids");

        ExternalContext externalContext = facesContext.getExternalContext();

        // SerializedView already created before within this request?
        SerializedView serializedView = (SerializedView) externalContext.getRequestMap()
            .get(SERIALIZED_VIEW_REQUEST_ATTR);
        if (serializedView == null) {
            if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - create new serialized view");

            // first call to saveSerializedView --> create SerializedView
            Object treeStruct = getTreeStructureToSave(facesContext);
            Object compStates = getComponentStateToSave(facesContext);
            serializedView = new StateManager.SerializedView(treeStruct, compStates);
            externalContext.getRequestMap().put(SERIALIZED_VIEW_REQUEST_ATTR,
                                                serializedView);

            if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - new serialized view created");
        }

        if (!isSavingStateInClient(facesContext)) {
            if (log.isTraceEnabled())
                log.trace("Processing saveSerializedView - server-side state saving - save state");
            //save state in server session
            saveSerializedViewInServletSession(facesContext, serializedView);

            if (log.isTraceEnabled()) log.trace("Exiting saveSerializedView - server-side state saving - saved state");
            Integer sequence = ViewSequenceUtils.getViewSequence(facesContext);
            return new SerializedView(sequence.toString(), null);
        }

        if (log.isTraceEnabled()) log.trace("Exiting saveSerializedView - client-side state saving");

        return serializedView;
    }

    private static void checkForDuplicateIds(FacesContext context,
                                             UIComponent component,
                                             Set ids) {
        String id = component.getId();
        if (id != null && !ids.add(id)) {
            throw new IllegalStateException("Client-id : " + id +
                " is duplicated in the faces tree. Component : " + component.getClientId(context) + ", path: " +
                getPathToComponent(component));
        }
        if (component instanceof NamingContainer)
        {
            ids = new HashSet();
        }
        Iterator it = component.getFacetsAndChildren();
        while (it.hasNext()) {
            UIComponent kid = (UIComponent) it.next();
            checkForDuplicateIds(context, kid, ids);
        }
    }

    private static String getPathToComponent(UIComponent component) {
        StringBuffer buf = new StringBuffer();

        if (component == null) {
            buf.append("{Component-Path : ");
            buf.append("[null]}");
            return buf.toString();
        }

        getPathToComponent(component, buf);

        buf.insert(0, "{Component-Path : ");
        buf.append("}");

        return buf.toString();
    }

    private static void getPathToComponent(UIComponent component, StringBuffer buf) {
        if (component == null)
            return;

        StringBuffer intBuf = new StringBuffer();

        intBuf.append("[Class: ");
        intBuf.append(component.getClass().getName());
        if (component instanceof UIViewRoot) {
            intBuf.append(",ViewId: ");
            intBuf.append(((UIViewRoot) component).getViewId());
        }
        else {
            intBuf.append(",Id: ");
            intBuf.append(component.getId());
        }
        intBuf.append("]");

        buf.insert(0, intBuf.toString());

        if (component != null) {
            getPathToComponent(component.getParent(), buf);
        }
    }

    public void writeState(FacesContext facesContext,
                           SerializedView serializedView) throws IOException {
        if (log.isTraceEnabled()) log.trace("Entering writeState");

        if (log.isTraceEnabled())
            log.trace("Processing writeState - either client-side (full state) or server-side (partial information; e.g. sequence)");
        if (serializedView != null) {
            UIViewRoot uiViewRoot = facesContext.getViewRoot();
            //save state in response (client-side: full state; server-side: sequence)
            RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, uiViewRoot.getRenderKitId());
            renderKit.getResponseStateManager().writeState(facesContext, serializedView);

            if (log.isTraceEnabled()) log.trace("Exiting writeState");
        }
    }

    /**
     * MyFaces extension
     *
     * @param facesContext
     * @param serializedView
     * @throws IOException
     */
    public void writeStateAsUrlParams(FacesContext facesContext,
                                      SerializedView serializedView) throws IOException {
        if (log.isTraceEnabled()) log.trace("Entering writeStateAsUrlParams");

        if (isSavingStateInClient(facesContext)) {
            if (log.isTraceEnabled())
                log.trace("Processing writeStateAsUrlParams - client-side state saving writing state");

            UIViewRoot uiViewRoot = facesContext.getViewRoot();
            //save state in response (client)
            RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, uiViewRoot.getRenderKitId());
            ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
            if (responseStateManager instanceof MyfacesResponseStateManager) {
                ((MyfacesResponseStateManager) responseStateManager).writeStateAsUrlParams(facesContext,
                                                                                           serializedView);
            }
            else {
                log.error("ResponseStateManager of render kit " + uiViewRoot.getRenderKitId() + " is no MyfacesResponseStateManager and does not support saving state in url parameters.");
            }
        }

        if (log.isTraceEnabled()) log.trace("Exiting writeStateAsUrlParams");
    }

    //helpers

    protected RenderKitFactory getRenderKitFactory() {
        if (_renderKitFactory == null) {
            _renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
        }
        return _renderKitFactory;
    }

    protected void saveSerializedViewInServletSession(FacesContext context,
                                                      SerializedView serializedView) {
        Map sessionMap = context.getExternalContext().getSessionMap();
        SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
            .get(SERIALIZED_VIEW_SESSION_ATTR);
        if (viewCollection == null) {
            viewCollection = new SerializedViewCollection();
            sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
        }
        viewCollection.add(context, serializeView(context, serializedView));
        // replace the value to notify the container about the change
        sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
    }

    protected SerializedView getSerializedViewFromServletSession(FacesContext context, String viewId, String sequenceStr) {
        ExternalContext externalContext = context.getExternalContext();
        Map requestMap = externalContext.getRequestMap();
        SerializedView serializedView = null;
        if (requestMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR)) {
            serializedView = (SerializedView) requestMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
        }
        else {
            SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
                .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
            if (viewCollection != null) {
                Integer sequence = null;
                if (sequenceStr == null) {
                    // use latest sequence
                    sequence = ViewSequenceUtils.getCurrentSequence(context);
                }
                else {
                    sequence = new Integer(sequenceStr);
                }
                if (sequence != null) {
                    Object state = viewCollection.get(sequence, viewId);
                    if (state != null) {
                        serializedView = deserializeView(state);
                    }
                }
            }
            requestMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
            ViewSequenceUtils.nextViewSequence(context);
        }
        return serializedView;
    }

    protected Object serializeView(FacesContext context, SerializedView serializedView) {
        if (log.isTraceEnabled()) log.trace("Entering serializeView");

        if (isSerializeStateInSession(context)) {
            if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize state in session");

            ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
            try {
                OutputStream os = baos;
                if (isCompressStateInSession(context)) {
                    if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize compressed");

                    os.write(COMPRESSED_FLAG);
                    os = new GZIPOutputStream(os, 1024);
                }
                else {
                    if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize uncompressed");

                    os.write(UNCOMPRESSED_FLAG);
                }
                ObjectOutputStream out = new ObjectOutputStream(os);
                out.writeObject(serializedView.getStructure());
                out.writeObject(serializedView.getState());
                out.close();
                baos.close();

                if (log.isTraceEnabled()) log.trace("Exiting serializeView - serialized. Bytes : " + baos.size());
                return baos.toByteArray();
            }
            catch (IOException e) {
                log.error("Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
                return null;
            }
        }
        else {
            if (log.isTraceEnabled()) log.trace("Exiting serializeView - do not serialize state in session.");
            return new Object[]{serializedView.getStructure(), serializedView.getState()};
        }
    }

    /**
     * Reads the value of the <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
     *
     * @param context <code>FacesContext</code> for the request we are processing.
     * @return boolean true, if the server state should be serialized in the session
     * @see SERIALIZE_STATE_IN_SESSION_PARAM
     */
    protected boolean isSerializeStateInSession(FacesContext context) {
        String value = context.getExternalContext().getInitParameter(
            SERIALIZE_STATE_IN_SESSION_PARAM);
        boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
        if (value != null) {
            serialize = new Boolean(value).booleanValue();
        }
        return serialize;
    }

    /**
     * Reads the value of the <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
     *
     * @param context <code>FacesContext</code> for the request we are processing.
     * @return boolean true, if the server state steam should be compressed
     * @see COMPRESS_SERVER_STATE_PARAM
     */
    protected boolean isCompressStateInSession(FacesContext context) {
        String value = context.getExternalContext().getInitParameter(
            COMPRESS_SERVER_STATE_PARAM);
        boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
        if (value != null) {
            compress = new Boolean(value).booleanValue();
        }
        return compress;
    }

    protected SerializedView deserializeView(Object state) {
        if (log.isTraceEnabled()) log.trace("Entering deserializeView");

        if (state instanceof byte[]) {
            if (log.isTraceEnabled())
                log.trace("Processing deserializeView - deserializing serialized state. Bytes : " + ((byte[]) state).length);

            try {
                ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
                InputStream is = bais;
                if (is.read() == COMPRESSED_FLAG) {
                    is = new GZIPInputStream(is);
                }
                ObjectInputStream in = new MyFacesObjectInputStream(
                    is);
                return new SerializedView(in.readObject(), in.readObject());
            }
            catch (IOException e) {
                log.error("Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
                return null;
            }
            catch (ClassNotFoundException e) {
                log.error("Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
                return null;
            }
        }
        else if (state instanceof Object[]) {
            if (log.isTraceEnabled()) log.trace("Exiting deserializeView - state not serialized.");

            Object[] value = (Object[]) state;
            return new SerializedView(value[0], value[1]);
        }
        else if (state == null) {
            log.error("Exiting deserializeView - this method should not be called with a null-state.");
            return null;
        }
        else {
            log.error("Exiting deserializeView - this method should not be called with a state of type : " + state.getClass());
            return null;
        }
    }

    protected static class SerializedViewCollection implements Serializable {
        private static final long serialVersionUID = -3734849062185115847L;

        private final List _keys = new ArrayList(DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
        private final Map _serializedViews = new HashMap();

        // old views will be hold as soft references which will be removed by
        // the garbage collector if free memory is low
        private transient Map _oldSerializedViews = null;

        public synchronized void add(FacesContext context, Object state) {
            Object key = new SerializedViewKey(context);
            _serializedViews.put(key, state);

            while (_keys.remove(key)) ;
            _keys.add(key);

            int views = getNumberOfViewsInSession(context);
            while (_keys.size() > views) {
                key = _keys.remove(0);
                Object oldView = _serializedViews.remove(key);
                if (oldView != null &&
                    !CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.equals(getCacheOldViewsInSessionMode(context)))
                {
                    getOldSerializedViewsMap().put(key, oldView);
                }
            }
        }

        /**
         * Reads the amount (default = 20) of views to be stored in session.
         *
         * @param context FacesContext for the current request, we are processing
         * @return Number vf views stored in the session
         * @see NUMBER_OF_VIEWS_IN_SESSION_PARAM
         */
        protected int getNumberOfViewsInSession(FacesContext context) {
            String value = context.getExternalContext().getInitParameter(
                NUMBER_OF_VIEWS_IN_SESSION_PARAM);
            int views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
            if (value != null) {
                try {
                    views = Integer.parseInt(value);
                    if (views <= 0) {
                        log.error("Configured value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
                            + " is not valid, must be an value > 0, using default value ("
                            + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
                        views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
                    }
                }
                catch (Throwable e) {
                    log.error("Error determining the value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
                        + ", expected an integer value > 0, using default value ("
                        + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
                }
            }
            return views;
        }

        /**
         * @return old serialized views map
         */
        protected Map getOldSerializedViewsMap() {
            FacesContext context = FacesContext.getCurrentInstance();
            if (_oldSerializedViews == null && context != null)
            {
                String cacheMode = getCacheOldViewsInSessionMode(context);
                if (CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT);
                }
            }
            return _oldSerializedViews;
        }
       
        /**
         * Reads the value of the <code>org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE</code> context parameter.
         *
         * @since 1.1.7
         * @param context
         * @return constant indicating caching mode
         * @see CACHE_OLD_VIEWS_IN_SESSION_MODE
         */
        protected String getCacheOldViewsInSessionMode(FacesContext context) {
            String value = context.getExternalContext().getInitParameter(
                    CACHE_OLD_VIEWS_IN_SESSION_MODE);
            if (value == null)
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK;
            }           
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT;
            }
            else
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
            }
        }
       
        public Object get(Integer sequence, String viewId) {
            Object key = new SerializedViewKey(viewId, sequence);
            Object value = _serializedViews.get(key);
            if (value == null)
            {
                Map oldSerializedViewMap = getOldSerializedViewsMap();
                if (oldSerializedViewMap != null)
                {
                    value = oldSerializedViewMap.get(key);
                }
            }
            return value;
        }
    }

    protected static class SerializedViewKey implements Serializable {
        private static final long serialVersionUID = -1170697124386063642L;

        private final String _viewId;
        private final Integer _sequenceId;

        public SerializedViewKey(String viewId, Integer sequence) {
            _sequenceId = sequence;
            _viewId = viewId;
        }

        public SerializedViewKey(FacesContext context) {
            _sequenceId = ViewSequenceUtils.getViewSequence(context);
            _viewId = context.getViewRoot().getViewId();
        }

        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (obj == this) {
                return true;
            }
            if (obj instanceof SerializedViewKey) {
                SerializedViewKey other = (SerializedViewKey) obj;
                return new EqualsBuilder().append(other._viewId, _viewId).append(other._sequenceId,
                                                                                 _sequenceId).isEquals();
            }
            return false;
        }

        public int hashCode() {
            return new HashCodeBuilder().append(_viewId).append(_sequenceId).toHashCode();
        }
    }
}
TOP

Related Classes of org.apache.myfaces.application.jsp.JspStateManagerImpl$SerializedViewKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.