org.apache.myfaces.application.viewstate.SerializedViewCollection.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.myfaces.application.viewstate.SerializedViewCollection.java

Source

/*
 * 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.viewstate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.FacesContext;
import org.apache.commons.collections.map.LRUMap;
import org.apache.myfaces.shared.util.WebConfigParamUtils;
import org.apache.myfaces.spi.ViewScopeProvider;

/**
 *
 */
class SerializedViewCollection implements Serializable {
    private static final Logger log = Logger.getLogger(SerializedViewCollection.class.getName());

    private static final Object[] EMPTY_STATES = new Object[] { null, null };

    private static final long serialVersionUID = -3734849062185115847L;
    private final List<SerializedViewKey> _keys = new ArrayList<SerializedViewKey>(
            ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
    private final Map<SerializedViewKey, Object> _serializedViews = new HashMap<SerializedViewKey, Object>();
    /**
     * The viewScopeIds can be shared between multiple entries of the same
     * view. To store it into session, the best is use two maps, one to 
     * associate the view key with the view scope id and other to keep track 
     * of the number of times the id is used. In that way it is possible to
     * know when a view scope id has been discarded and destroy the view scope
     * in the right time.
     */
    private HashMap<SerializedViewKey, String> _viewScopeIds = null;
    private HashMap<String, Integer> _viewScopeIdCounts = null;

    private final Map<SerializedViewKey, SerializedViewKey> _precedence = new HashMap<SerializedViewKey, SerializedViewKey>();
    private Map<String, SerializedViewKey> _lastWindowKeys = null;

    public void put(FacesContext context, Object state, SerializedViewKey key,
            SerializedViewKey previousRestoredKey) {
        put(context, state, key, previousRestoredKey, null, null);
    }

    public synchronized void put(FacesContext context, Object state, SerializedViewKey key,
            SerializedViewKey previousRestoredKey, ViewScopeProvider viewScopeProvider, String viewScopeId) {
        if (state == null) {
            state = EMPTY_STATES;
        } else if (state instanceof Object[] && ((Object[]) state).length == 2 && ((Object[]) state)[0] == null
                && ((Object[]) state)[1] == null) {
            // The generated state can be considered zero, set it as null
            // into the map.
            state = null;
        }

        if (_serializedViews.containsKey(key)) {
            // Update the state, the viewScopeId does not change.
            _serializedViews.put(key, state);
            return;
        }

        Integer maxCount = getNumberOfSequentialViewsInSession(context);
        if (maxCount != null) {
            if (previousRestoredKey != null) {
                if (!_serializedViews.isEmpty()) {
                    _precedence.put((SerializedViewKey) key, previousRestoredKey);
                } else {
                    // Note when the session is invalidated, _serializedViews map is empty,
                    // but we could have a not null previousRestoredKey (the last one before
                    // invalidate the session), so we need to check that condition before
                    // set the precence. In that way, we ensure the precedence map will always
                    // have valid keys.
                    previousRestoredKey = null;
                }
            }
        }
        _serializedViews.put(key, state);

        if (viewScopeProvider != null && viewScopeId != null) {
            if (_viewScopeIds == null) {
                _viewScopeIds = new HashMap<SerializedViewKey, String>();
            }
            _viewScopeIds.put(key, viewScopeId);
            if (_viewScopeIdCounts == null) {
                _viewScopeIdCounts = new HashMap<String, Integer>();
            }
            Integer vscount = _viewScopeIdCounts.get(viewScopeId);
            vscount = (vscount == null) ? 1 : vscount + 1;
            _viewScopeIdCounts.put(viewScopeId, vscount);
        }

        while (_keys.remove(key)) {
            // do nothing
        }
        _keys.add(key);

        if (previousRestoredKey != null && maxCount != null && maxCount > 0) {
            int count = 0;
            SerializedViewKey previousKey = (SerializedViewKey) key;
            do {
                previousKey = _precedence.get(previousKey);
                count++;
            } while (previousKey != null && count < maxCount);

            if (previousKey != null) {
                SerializedViewKey keyToRemove = (SerializedViewKey) previousKey;
                // In theory it should be only one key but just to be sure
                // do it in a loop, but in this case if cache old views is on,
                // put on that map.
                do {
                    while (_keys.remove(keyToRemove)) {
                        // do nothing
                    }

                    _serializedViews.remove(keyToRemove);

                    if (viewScopeProvider != null && _viewScopeIds != null) {
                        String oldViewScopeId = _viewScopeIds.remove(keyToRemove);
                        if (oldViewScopeId != null) {
                            Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
                            vscount = vscount - 1;
                            if (vscount != null && vscount.intValue() < 1) {
                                _viewScopeIdCounts.remove(oldViewScopeId);
                                viewScopeProvider.destroyViewScopeMap(context, oldViewScopeId);
                            } else {
                                _viewScopeIdCounts.put(oldViewScopeId, vscount);
                            }
                        }
                    }

                    keyToRemove = _precedence.remove(keyToRemove);
                } while (keyToRemove != null);
            }
        }
        int views = getNumberOfViewsInSession(context);
        while (_keys.size() > views) {
            key = _keys.remove(0);
            if (maxCount != null && maxCount > 0) {
                SerializedViewKey keyToRemove = (SerializedViewKey) key;
                // Note in this case the key to delete is the oldest one,
                // so it could be at least one precedence, but to be safe
                // do it with a loop.
                do {
                    keyToRemove = _precedence.remove(keyToRemove);
                } while (keyToRemove != null);
            }

            _serializedViews.remove(key);

            if (viewScopeProvider != null && _viewScopeIds != null) {
                String oldViewScopeId = _viewScopeIds.remove(key);
                if (oldViewScopeId != null) {
                    Integer vscount = _viewScopeIdCounts.get(oldViewScopeId);
                    vscount = vscount - 1;
                    if (vscount != null && vscount.intValue() < 1) {
                        _viewScopeIdCounts.remove(oldViewScopeId);
                        viewScopeProvider.destroyViewScopeMap(context, oldViewScopeId);
                    } else {
                        _viewScopeIdCounts.put(oldViewScopeId, vscount);
                    }
                }
            }
        }
    }

    protected Integer getNumberOfSequentialViewsInSession(FacesContext context) {
        return WebConfigParamUtils.getIntegerInitParameter(context.getExternalContext(),
                ServerSideStateCacheImpl.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
    }

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

    public synchronized void putLastWindowKey(FacesContext context, String id, SerializedViewKey key) {
        if (_lastWindowKeys == null) {
            Integer i = getNumberOfSequentialViewsInSession(context);
            int j = getNumberOfViewsInSession(context);
            if (i != null && i.intValue() > 0) {
                _lastWindowKeys = new LRUMap((j / i.intValue()) + 1);
            } else {
                _lastWindowKeys = new LRUMap(j + 1);
            }
        }
        _lastWindowKeys.put(id, key);
    }

    public SerializedViewKey getLastWindowKey(FacesContext context, String id) {
        if (_lastWindowKeys != null) {
            return _lastWindowKeys.get(id);
        }
        return null;
    }

    public Object get(SerializedViewKey key) {
        Object value = _serializedViews.get(key);
        if (value == null) {
            if (_serializedViews.containsKey(key)) {
                return EMPTY_STATES;
            }
        } else if (value instanceof Object[] && ((Object[]) value).length == 2 && ((Object[]) value)[0] == null
                && ((Object[]) value)[1] == null) {
            // Remember inside the state map null is stored as an empty array.
            return null;
        }
        return value;
    }
}