View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.application.viewstate;
20  
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  import javax.faces.context.FacesContext;
29  import org.apache.commons.collections.map.AbstractReferenceMap;
30  import org.apache.commons.collections.map.ReferenceMap;
31  import org.apache.myfaces.shared.util.WebConfigParamUtils;
32  
33  /**
34   *
35   */
36  class SerializedViewCollection implements Serializable
37  {
38      private static final Logger log = Logger.getLogger(SerializedViewCollection.class.getName());
39  
40      private static final Object[] EMPTY_STATES = new Object[]{null, null};
41  
42      private static final long serialVersionUID = -3734849062185115847L;
43      private final List<SerializedViewKey> _keys = 
44          new ArrayList<SerializedViewKey>(
45              ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
46      private final Map<SerializedViewKey, Object> _serializedViews = 
47          new HashMap<SerializedViewKey, Object>();
48  
49      private final Map<SerializedViewKey, SerializedViewKey> _precedence =
50          new HashMap<SerializedViewKey, SerializedViewKey>();
51  
52      // old views will be hold as soft references which will be removed by
53      // the garbage collector if free memory is low
54      private transient Map<Object, Object> _oldSerializedViews = null;
55  
56      public synchronized void add(FacesContext context, Object state, 
57          SerializedViewKey key, SerializedViewKey previousRestoredKey)
58      {
59          if (state == null)
60          {
61              state = EMPTY_STATES;
62          }
63          else if (state instanceof Object[] &&
64              ((Object[])state).length == 2 &&
65              ((Object[])state)[0] == null &&
66              ((Object[])state)[1] == null)
67          {
68              // The generated state can be considered zero, set it as null
69              // into the map.
70              state = null;
71          }
72  
73          Integer maxCount = getNumberOfSequentialViewsInSession(context);
74          if (maxCount != null)
75          {
76              if (previousRestoredKey != null)
77              {
78                  if (!_serializedViews.isEmpty())
79                  {
80                      _precedence.put((SerializedViewKey) key, previousRestoredKey);
81                  }
82                  else
83                  {
84                      // Note when the session is invalidated, _serializedViews map is empty,
85                      // but we could have a not null previousRestoredKey (the last one before
86                      // invalidate the session), so we need to check that condition before
87                      // set the precence. In that way, we ensure the precedence map will always
88                      // have valid keys.
89                      previousRestoredKey = null;
90                  }
91              }
92          }
93          _serializedViews.put(key, state);
94  
95          while (_keys.remove(key))
96          {
97              // do nothing
98          }
99          _keys.add(key);
100 
101         if (previousRestoredKey != null && maxCount != null && maxCount > 0)
102         {
103             int count = 0;
104             SerializedViewKey previousKey = (SerializedViewKey) key;
105             do
106             {
107                 previousKey = _precedence.get(previousKey);
108                 count++;
109             }
110             while (previousKey != null && count < maxCount);
111 
112             if (previousKey != null)
113             {
114                 SerializedViewKey keyToRemove = (SerializedViewKey) previousKey;
115                 // In theory it should be only one key but just to be sure
116                 // do it in a loop, but in this case if cache old views is on,
117                 // put on that map.
118                 do
119                 {
120                     while (_keys.remove(keyToRemove))
121                     {
122                         // do nothing
123                     }
124 
125                     if (_serializedViews.containsKey(keyToRemove) &&
126                         !ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.
127                                 equals( getCacheOldViewsInSessionMode(context)) )
128                     {
129                         getOldSerializedViewsMap().put(keyToRemove, _serializedViews.remove(keyToRemove));
130                     }
131                     else
132                     {
133                         _serializedViews.remove(keyToRemove);
134                     }
135 
136                     keyToRemove = _precedence.remove(keyToRemove);
137                 }
138                 while (keyToRemove != null);
139             }
140         }
141         int views = getNumberOfViewsInSession(context);
142         while (_keys.size() > views)
143         {
144             key = _keys.remove(0);
145             if (maxCount != null && maxCount > 0)
146             {
147                 SerializedViewKey keyToRemove = (SerializedViewKey) key;
148                 // Note in this case the key to delete is the oldest one,
149                 // so it could be at least one precedence, but to be safe
150                 // do it with a loop.
151                 do
152                 {
153                     keyToRemove = _precedence.remove(keyToRemove);
154                 }
155                 while (keyToRemove != null);
156             }
157             if (_serializedViews.containsKey(key) &&
158                 !ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.
159                         equals( getCacheOldViewsInSessionMode( context )))
160             {
161 
162                 getOldSerializedViewsMap().put(key, _serializedViews.remove(key));
163             }
164             else
165             {
166                 _serializedViews.remove(key);
167             }
168         }
169     }
170 
171     protected Integer getNumberOfSequentialViewsInSession(FacesContext context)
172     {
173         return WebConfigParamUtils.getIntegerInitParameter( context.getExternalContext(),
174                 ServerSideStateCacheImpl.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
175     }
176 
177     /**
178      * Reads the amount (default = 20) of views to be stored in session.
179      * @see ServerSideStateCacheImpl#NUMBER_OF_VIEWS_IN_SESSION_PARAM
180      * @param context FacesContext for the current request, we are processing
181      * @return Number vf views stored in the session
182      */
183     protected int getNumberOfViewsInSession(FacesContext context)
184     {
185         String value = context.getExternalContext().getInitParameter(
186                 ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM);
187         int views = ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
188         if (value != null)
189         {
190             try
191             {
192                 views = Integer.parseInt(value);
193                 if (views <= 0)
194                 {
195                     log.severe("Configured value for " + ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM
196                               + " is not valid, must be an value > 0, using default value ("
197                               + ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
198                     views = ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
199                 }
200             }
201             catch (Throwable e)
202             {
203                 log.log( Level.SEVERE, "Error determining the value for "
204                        + ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM
205                        + ", expected an integer value > 0, using default value ("
206                        + ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
207             }
208         }
209         return views;
210     }
211 
212     /**
213      * @return old serialized views map
214      */
215     @SuppressWarnings("unchecked")
216     protected Map<Object, Object> getOldSerializedViewsMap()
217     {
218         FacesContext context = FacesContext.getCurrentInstance();
219         if (_oldSerializedViews == null && context != null)
220         {
221             String cacheMode = getCacheOldViewsInSessionMode(context);
222             if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK.equals(cacheMode))
223             {
224                 _oldSerializedViews = new ReferenceMap( AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
225             }
226             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK.equals(cacheMode))
227             {
228                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true);
229             }
230             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT.equals(cacheMode))
231             {
232                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true);
233             }
234             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT.equals(cacheMode))
235             {
236                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT);
237             }
238         }
239 
240         return _oldSerializedViews;
241     }
242 
243     /**
244      * Reads the value of the <code>org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE</code> context parameter.
245      *
246      * @since 1.2.5
247      * @param context
248      * @return constant indicating caching mode
249      * @see ServerSideStateCacheImpl#CACHE_OLD_VIEWS_IN_SESSION_MODE
250      */
251     protected String getCacheOldViewsInSessionMode(FacesContext context)
252     {
253         String value = context.getExternalContext().getInitParameter(
254                 ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE);
255         if (value == null)
256         {
257             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
258         }
259         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT))
260         {
261             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT;
262         }
263         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK))
264         {
265             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK;
266         }
267         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK))
268         {
269             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK;
270         }
271         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT))
272         {
273             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT;
274         }
275         else
276         {
277             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
278         }
279     }
280 
281     public Object get(SerializedViewKey key)
282     {
283         Object value = _serializedViews.get(key);
284         if (value == null)
285         {
286             if (_serializedViews.containsKey(key))
287             {
288                 return EMPTY_STATES;
289             }
290             Map<Object,Object> oldSerializedViewMap = getOldSerializedViewsMap();
291             if (oldSerializedViewMap != null)
292             {
293                 value = oldSerializedViewMap.get(key);
294                 if (value == null && oldSerializedViewMap.containsKey(key) )
295                 {
296                     return EMPTY_STATES;
297                 }
298             }
299         }
300         else if (value instanceof Object[] &&
301             ((Object[])value).length == 2 &&
302             ((Object[])value)[0] == null &&
303             ((Object[])value)[1] == null)
304         {
305             // Remember inside the state map null is stored as an empty array.
306             return null;
307         }
308         return value;
309     }
310 }