Java tutorial
/* * 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; } }