| /* |
| * 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.Logger; |
| import javax.faces.context.FacesContext; |
| import org.apache.myfaces.config.MyfacesConfig; |
| import org.apache.myfaces.spi.ViewScopeProvider; |
| import org.apache.myfaces.util.lang.LRULinkedHashMap; |
| |
| /** |
| * |
| */ |
| 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<>(MyfacesConfig.NUMBER_OF_VIEWS_IN_SESSION_DEFAULT); |
| private final Map<SerializedViewKey, Object> _serializedViews = new HashMap<>(); |
| |
| /** |
| * 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<>(); |
| 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); |
| // Make sure the view is at the end of the discard queue |
| while (_keys.remove(key)) |
| { |
| // do nothing |
| } |
| _keys.add(key); |
| 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<>(); |
| } |
| _viewScopeIds.put(key, viewScopeId); |
| if (_viewScopeIdCounts == null) |
| { |
| _viewScopeIdCounts = new HashMap<>(); |
| } |
| 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 < 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 < 1) |
| { |
| _viewScopeIdCounts.remove(oldViewScopeId); |
| viewScopeProvider.destroyViewScopeMap(context, oldViewScopeId); |
| } |
| else |
| { |
| _viewScopeIdCounts.put(oldViewScopeId, vscount); |
| } |
| } |
| } |
| } |
| } |
| |
| protected Integer getNumberOfSequentialViewsInSession(FacesContext context) |
| { |
| return MyfacesConfig.getCurrentInstance(context).getNumberOfSequentialViewsInSession(); |
| } |
| |
| /** |
| * 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) |
| { |
| return MyfacesConfig.getCurrentInstance(context).getNumberOfViewsInSession(); |
| } |
| |
| 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> 0) |
| { |
| _lastWindowKeys = new LRULinkedHashMap<>((j / i) + 1); |
| } |
| else |
| { |
| _lastWindowKeys = new LRULinkedHashMap(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; |
| } |
| } |