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                         Object removedValue = _serializedViews.remove(keyToRemove);
130                         if (removedValue == null)
131                         {
132                             removedValue = EMPTY_STATES;
133                         }
134                         getOldSerializedViewsMap().put(keyToRemove, removedValue);
135                     }
136                     else
137                     {
138                         _serializedViews.remove(keyToRemove);
139                     }
140 
141                     keyToRemove = _precedence.remove(keyToRemove);
142                 }
143                 while (keyToRemove != null);
144             }
145         }
146         int views = getNumberOfViewsInSession(context);
147         while (_keys.size() > views)
148         {
149             key = _keys.remove(0);
150             if (maxCount != null && maxCount > 0)
151             {
152                 SerializedViewKey keyToRemove = (SerializedViewKey) key;
153                 // Note in this case the key to delete is the oldest one,
154                 // so it could be at least one precedence, but to be safe
155                 // do it with a loop.
156                 do
157                 {
158                     keyToRemove = _precedence.remove(keyToRemove);
159                 }
160                 while (keyToRemove != null);
161             }
162             if (_serializedViews.containsKey(key) &&
163                 !ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.
164                         equals( getCacheOldViewsInSessionMode( context )))
165             {
166                 Object removedValue = _serializedViews.remove(key);
167                 if (removedValue == null)
168                 {
169                     removedValue = EMPTY_STATES;
170                 }
171                 getOldSerializedViewsMap().put(key, removedValue);
172             }
173             else
174             {
175                 _serializedViews.remove(key);
176             }
177         }
178     }
179 
180     protected Integer getNumberOfSequentialViewsInSession(FacesContext context)
181     {
182         return WebConfigParamUtils.getIntegerInitParameter( context.getExternalContext(),
183                 ServerSideStateCacheImpl.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
184     }
185 
186     /**
187      * Reads the amount (default = 20) of views to be stored in session.
188      * @see ServerSideStateCacheImpl#NUMBER_OF_VIEWS_IN_SESSION_PARAM
189      * @param context FacesContext for the current request, we are processing
190      * @return Number vf views stored in the session
191      */
192     protected int getNumberOfViewsInSession(FacesContext context)
193     {
194         String value = context.getExternalContext().getInitParameter(
195                 ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM);
196         int views = ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
197         if (value != null)
198         {
199             try
200             {
201                 views = Integer.parseInt(value);
202                 if (views <= 0)
203                 {
204                     log.severe("Configured value for " + ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM
205                               + " is not valid, must be an value > 0, using default value ("
206                               + ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
207                     views = ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
208                 }
209             }
210             catch (Throwable e)
211             {
212                 log.log( Level.SEVERE, "Error determining the value for "
213                        + ServerSideStateCacheImpl.NUMBER_OF_VIEWS_IN_SESSION_PARAM
214                        + ", expected an integer value > 0, using default value ("
215                        + ServerSideStateCacheImpl.DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
216             }
217         }
218         return views;
219     }
220 
221     /**
222      * @return old serialized views map
223      */
224     @SuppressWarnings("unchecked")
225     protected Map<Object, Object> getOldSerializedViewsMap()
226     {
227         FacesContext context = FacesContext.getCurrentInstance();
228         if (_oldSerializedViews == null && context != null)
229         {
230             String cacheMode = getCacheOldViewsInSessionMode(context);
231             if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK.equals(cacheMode))
232             {
233                 _oldSerializedViews = new ReferenceMap( AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
234             }
235             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK.equals(cacheMode))
236             {
237                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true);
238             }
239             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT.equals(cacheMode))
240             {
241                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true);
242             }
243             else if ( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT.equals(cacheMode))
244             {
245                 _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT);
246             }
247         }
248 
249         return _oldSerializedViews;
250     }
251 
252     /**
253      * Reads the value of the <code>org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE</code> context parameter.
254      *
255      * @since 1.2.5
256      * @param context
257      * @return constant indicating caching mode
258      * @see ServerSideStateCacheImpl#CACHE_OLD_VIEWS_IN_SESSION_MODE
259      */
260     protected String getCacheOldViewsInSessionMode(FacesContext context)
261     {
262         String value = context.getExternalContext().getInitParameter(
263                 ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE);
264         if (value == null)
265         {
266             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
267         }
268         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT))
269         {
270             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT;
271         }
272         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK))
273         {
274             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK;
275         }
276         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK))
277         {
278             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK;
279         }
280         else if (value.equalsIgnoreCase( ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT))
281         {
282             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT;
283         }
284         else
285         {
286             return ServerSideStateCacheImpl.CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
287         }
288     }
289 
290     public Object get(SerializedViewKey key)
291     {
292         Object value = _serializedViews.get(key);
293         if (value == null)
294         {
295             if (_serializedViews.containsKey(key))
296             {
297                 return EMPTY_STATES;
298             }
299             Map<Object,Object> oldSerializedViewMap = getOldSerializedViewsMap();
300             if (oldSerializedViewMap != null)
301             {
302                 value = oldSerializedViewMap.get(key);
303                 if (value == null && oldSerializedViewMap.containsKey(key) )
304                 {
305                     return EMPTY_STATES;
306                 }
307             }
308         }
309         else if (value instanceof Object[] &&
310             ((Object[])value).length == 2 &&
311             ((Object[])value)[0] == null &&
312             ((Object[])value)[1] == null)
313         {
314             // Remember inside the state map null is stored as an empty array.
315             return null;
316         }
317         return value;
318     }
319 }