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.view.facelets.pool.impl;
20  
21  import java.util.ConcurrentModificationException;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  import javax.faces.component.UIViewRoot;
26  import javax.faces.context.FacesContext;
27  import org.apache.myfaces.context.RequestViewContext;
28  import org.apache.myfaces.shared.util.WebConfigParamUtils;
29  import org.apache.myfaces.view.facelets.pool.RestoreViewFromPoolResult;
30  import org.apache.myfaces.view.facelets.pool.ViewPool;
31  import org.apache.myfaces.view.facelets.pool.ViewEntry;
32  import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata;
33  import org.apache.myfaces.view.facelets.tag.jsf.FaceletState;
34  
35  /**
36   *
37   * @author Leonardo Uribe
38   */
39  public class ViewPoolImpl extends ViewPool
40  {
41  
42      
43      private static final String SKIP_VIEW_MAP_SAVE_STATE = "oam.viewPool.SKIP_VIEW_MAP_SAVE_STATE";    
44              
45      private Map<MetadataViewKey, ViewPoolEntryHolder > staticStructureViewPool;
46      
47      private Map<MetadataViewKey, Map<DynamicViewKey, ViewPoolEntryHolder>> dynamicStructureViewPool;
48      
49      private Map<MetadataViewKey, ViewPoolEntryHolder > partialStructureViewPool;
50      
51      private final int maxCount;
52      private final int dynamicPartialLimit;
53      
54      private final boolean entryWeak;
55      private final boolean deferredNavigation;
56      
57      // View metadata
58      private Map<MetadataViewKey, ViewStructureMetadata> staticStructureViewMetadataMap;
59      private Map<MetadataViewKey, Map<DynamicViewKey, ViewStructureMetadata>> 
60              dynamicStructureViewMetadataMap;
61      
62      public ViewPoolImpl(FacesContext facesContext, Map<String, String> parameters)
63      {
64          staticStructureViewPool = new ConcurrentHashMap<MetadataViewKey, ViewPoolEntryHolder>();
65          partialStructureViewPool = new ConcurrentHashMap<MetadataViewKey, ViewPoolEntryHolder>();
66          dynamicStructureViewPool = new ConcurrentHashMap<MetadataViewKey, Map<DynamicViewKey, ViewPoolEntryHolder>>();
67          maxCount = WebConfigParamUtils.getIntegerInitParameter(facesContext.getExternalContext(),
68                  INIT_PARAM_VIEW_POOL_MAX_POOL_SIZE, 
69                  parameters.containsKey(INIT_PARAM_VIEW_POOL_MAX_POOL_SIZE) ? 
70                      Integer.parseInt(parameters.get(INIT_PARAM_VIEW_POOL_MAX_POOL_SIZE)) :
71                      INIT_PARAM_VIEW_POOL_MAX_POOL_SIZE_DEFAULT);
72          dynamicPartialLimit = WebConfigParamUtils.getIntegerInitParameter(facesContext.getExternalContext(),
73                  INIT_PARAM_VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT, 
74                  parameters.containsKey(INIT_PARAM_VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT) ?
75                  Integer.parseInt(parameters.get(INIT_PARAM_VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT)) : 
76                  INIT_PARAM_VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT_DEFAULT);
77          String entryMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
78                  INIT_PARAM_VIEW_POOL_ENTRY_MODE,
79                  parameters.containsKey(INIT_PARAM_VIEW_POOL_ENTRY_MODE) ?
80                  parameters.get(INIT_PARAM_VIEW_POOL_ENTRY_MODE) :
81                  INIT_PARAM_VIEW_POOL_ENTRY_MODE_DEFAULT);
82          entryWeak = ENTRY_MODE_WEAK.equals(entryMode);
83          String deferredNavigationVal = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
84                  INIT_PARAM_VIEW_POOL_DEFERRED_NAVIGATION,
85                  parameters.containsKey(INIT_PARAM_VIEW_POOL_DEFERRED_NAVIGATION) ?
86                  parameters.get(INIT_PARAM_VIEW_POOL_DEFERRED_NAVIGATION) :
87                  "false");
88          deferredNavigation = Boolean.valueOf(deferredNavigationVal);
89          
90          staticStructureViewMetadataMap = new ConcurrentHashMap<MetadataViewKey, ViewStructureMetadata>();
91          dynamicStructureViewMetadataMap = new ConcurrentHashMap<MetadataViewKey, 
92                  Map<DynamicViewKey, ViewStructureMetadata>>();
93      }
94      
95      protected void pushStaticStructureView(FacesContext context, MetadataViewKey key, ViewEntry entry)
96      {
97          ViewPoolEntryHolder q = staticStructureViewPool.get(key);
98          if (q == null)
99          {
100             q = new ViewPoolEntryHolder(maxCount);
101             staticStructureViewPool.put(key, q);
102         }
103         q.add(entry);
104     }
105     
106     protected ViewEntry popStaticStructureView(FacesContext context, MetadataViewKey key)
107     {
108         ViewPoolEntryHolder q = staticStructureViewPool.get(key);
109         if (q == null)
110         {
111             return null;
112         }
113         ViewEntry entry = q.poll();
114         if (entry == null)
115         {
116             return null;
117         }
118         do
119         {
120             if (entry.activate())
121             {
122                 return entry;
123             }
124             entry = q.poll();
125         }
126         while (entry != null);
127         return null;
128     }
129     
130     protected void pushPartialStructureView(FacesContext context, MetadataViewKey key, ViewEntry entry)
131     {
132         ViewPoolEntryHolder q = partialStructureViewPool.get(key);
133         if (q == null)
134         {
135             q = new ViewPoolEntryHolder(maxCount);
136             partialStructureViewPool.put(key, q);
137         }
138         q.add(entry);
139     }
140     
141     protected ViewEntry popPartialStructureView(FacesContext context, MetadataViewKey key)
142     {
143         ViewPoolEntryHolder q = partialStructureViewPool.get(key);
144         if (q == null)
145         {
146             return null;
147         }
148         ViewEntry entry = q.poll();
149         if (entry == null)
150         {
151             return null;
152         }
153         do
154         {
155             if (entry.activate())
156             {
157                 return entry;
158             }
159             entry = q.poll();
160         }while (entry != null);
161         return null;
162     }
163 
164     /**
165      * Generates an unique key according to the metadata information stored
166      * in the passed UIViewRoot instance that can affect the way how the view is generated. 
167      * By default, the "view" params are the viewId, the locale, the renderKit and 
168      * the contracts associated to the view.
169      * 
170      * @param facesContext
171      * @param root
172      * @return 
173      */
174     protected MetadataViewKey deriveViewKey(FacesContext facesContext,
175                     UIViewRoot root)
176     {
177         MetadataViewKey viewKey; 
178         if (!facesContext.getResourceLibraryContracts().isEmpty())
179         {
180             String[] contracts = new String[facesContext.getResourceLibraryContracts().size()];
181             contracts = facesContext.getResourceLibraryContracts().toArray(contracts);
182             viewKey = new MetadataViewKeyImpl(root.getViewId(), root.getRenderKitId(), root.getLocale(), contracts);
183         }
184         else
185         {
186             viewKey = new MetadataViewKeyImpl(root.getViewId(), root.getRenderKitId(), root.getLocale());
187         }
188         return viewKey;
189     }
190 
191     protected ViewEntry generateViewEntry(FacesContext facesContext,
192                     UIViewRoot root)
193     {
194         return entryWeak ? new WeakViewEntry(root) : new SoftViewEntry(root);
195     }
196 
197     protected DynamicViewKey generateDynamicStructureViewKey(FacesContext facesContext, UIViewRoot root,
198             FaceletState faceletDynamicState)
199     {
200         return new DynamicViewKey(faceletDynamicState);
201     }
202 
203     protected void pushDynamicStructureView(FacesContext context, UIViewRoot root, DynamicViewKey key, ViewEntry entry)
204     {
205         MetadataViewKey ordinaryKey = deriveViewKey(context, root);
206         Map<DynamicViewKey, ViewPoolEntryHolder> map = dynamicStructureViewPool.get(ordinaryKey);
207         if (map == null)
208         {
209             map = new ConcurrentHashMap<DynamicViewKey, ViewPoolEntryHolder>();
210             dynamicStructureViewPool.put(ordinaryKey, map);
211         }
212         ViewPoolEntryHolder q = map.get(key);
213         if (q == null)
214         {
215             q = new ViewPoolEntryHolder(maxCount);
216             map.put(key, q);
217         }
218         if (!q.add(entry))
219         {
220             pushPartialStructureView(context, ordinaryKey, entry);
221         }
222     }
223 
224     protected ViewEntry popDynamicStructureView(FacesContext context, UIViewRoot root, DynamicViewKey key)
225     {
226         MetadataViewKey ordinaryKey = deriveViewKey(context, root);
227         Map<DynamicViewKey, ViewPoolEntryHolder> map = dynamicStructureViewPool.get(ordinaryKey);
228         if (map == null)
229         {
230             return null;
231         }
232         ViewPoolEntryHolder q = map.get(key);
233         if (q == null)
234         {
235             return null;
236         }
237         ViewEntry entry = q.poll();
238         while (entry != null)
239         {
240             if (entry.activate())
241             {
242                 return entry;
243             }
244             entry = q.poll();
245         }
246         return null;
247     }
248 
249     @Override
250     public void pushStaticStructureView(FacesContext context, UIViewRoot root)
251     {
252         MetadataViewKey key = deriveViewKey(context, root);
253         if (staticStructureViewMetadataMap.containsKey(key))
254         {
255             ViewEntry value = generateViewEntry(context, root);
256             pushStaticStructureView(context, key, value);
257         }
258     }
259 
260     @Override
261     public ViewEntry popStaticOrPartialStructureView(FacesContext context, UIViewRoot root)
262     {
263         MetadataViewKey key = deriveViewKey(context, root);
264         ViewEntry entry = popStaticStructureView(context, key);
265         if (entry != null)
266         {
267             entry.setResult(RestoreViewFromPoolResult.COMPLETE);
268         }
269         else
270         {
271             entry = popPartialStructureView(context, key);
272             if (entry != null)
273             {
274                 entry.setResult(RestoreViewFromPoolResult.REFRESH_REQUIRED);
275             }
276             else
277             {
278                 Map<DynamicViewKey, ViewPoolEntryHolder> map = dynamicStructureViewPool.get(key);
279                 if (map != null)
280                 {
281                     try
282                     {
283                         ViewPoolEntryHolder maxEntry = null;
284                         long max = -1;
285                         for (Iterator<ViewPoolEntryHolder> it = map.values().iterator(); it.hasNext();)
286                         {
287                             ViewPoolEntryHolder e = it.next();
288                             long count = e.getCount();
289                             if (count > max && count > dynamicPartialLimit)
290                             {
291                                 maxEntry = e;
292                                 max = count;
293                             }
294                         }
295                         if (maxEntry != null)
296                         {
297                             entry = maxEntry.poll();
298                             if (entry != null)
299                             {
300                                 do
301                                 {
302                                     if (entry.activate())
303                                     {
304                                         break;
305                                     }
306                                     entry = maxEntry.poll();
307                                 }
308                                 while (entry != null);
309                                 if (entry != null)
310                                 {
311                                     entry.setResult(RestoreViewFromPoolResult.REFRESH_REQUIRED);
312                                 }
313                             }
314                         }
315                     }
316                     catch(ConcurrentModificationException ex)
317                     {
318                         //do nothing
319                     }
320                 }
321             }
322         }
323         return entry;
324     }
325 
326     @Override
327     public void pushDynamicStructureView(FacesContext context, UIViewRoot root, 
328             FaceletState faceletDynamicState)
329     {
330         DynamicViewKey key = (DynamicViewKey) generateDynamicStructureViewKey(context, root, faceletDynamicState);
331         MetadataViewKey ordinaryKey = deriveViewKey(context, root);
332         Map<DynamicViewKey, ViewStructureMetadata> map = dynamicStructureViewMetadataMap.get(ordinaryKey);
333         if (map != null)
334         {
335             ViewEntry value = generateViewEntry(context, root);
336             pushDynamicStructureView(context, root, key, value);
337         }        
338     }
339 
340     @Override
341     public ViewEntry popDynamicStructureView(FacesContext context, UIViewRoot root, 
342             FaceletState faceletDynamicState)
343     {
344         DynamicViewKey key = generateDynamicStructureViewKey(context, root, faceletDynamicState);
345         ViewEntry entry = popDynamicStructureView(context, root, key);
346         if (entry != null)
347         {
348             entry.setResult(RestoreViewFromPoolResult.COMPLETE);
349         }
350         return entry;
351     }
352 
353     @Override
354     public void pushPartialStructureView(FacesContext context, UIViewRoot root)
355     {
356         MetadataViewKey key = deriveViewKey(context, root);
357         ViewEntry value = generateViewEntry(context, root);
358         pushPartialStructureView(context, key, value);
359     }
360 
361     @Override
362     public boolean isWorthToRecycleThisView(FacesContext context, UIViewRoot root)
363     {
364         MetadataViewKey key = deriveViewKey(context, root);
365         ViewPoolEntryHolder q = partialStructureViewPool.get(key);
366         if (q != null && q.isFull())
367         {
368             return false;
369         }
370         return true;
371     }
372 
373     @Override
374     public void storeStaticViewStructureMetadata(FacesContext context, UIViewRoot root,
375                 FaceletState faceletState)
376     {
377         MetadataViewKey key = deriveViewKey(context, root);
378         if (!staticStructureViewMetadataMap.containsKey(key))
379         {
380             RequestViewContext rvc = RequestViewContext.getCurrentInstance(context);
381             Object state = saveViewRootState(context, root);
382             ViewStructureMetadata metadata = new ViewStructureMetadataImpl(state, 
383                     rvc.getRequestViewMetadata().cloneInstance());
384             staticStructureViewMetadataMap.put(key, metadata);
385         }
386     }
387 
388     @Override
389     public ViewStructureMetadata retrieveStaticViewStructureMetadata(FacesContext context, UIViewRoot root)
390     {
391         MetadataViewKey key = deriveViewKey(context, root);
392         return staticStructureViewMetadataMap.get(key);
393     }
394 
395     private Object saveViewRootState(FacesContext context, UIViewRoot root)
396     {
397         Object state;
398         if (root.getViewMap(false) != null)
399         {
400             try
401             {
402                 context.getAttributes().put(SKIP_VIEW_MAP_SAVE_STATE, Boolean.TRUE);
403                 state = root.saveState(context);
404             }
405             finally
406             {
407                 context.getAttributes().remove(SKIP_VIEW_MAP_SAVE_STATE);
408             }
409         }
410         else
411         {
412             state = root.saveState(context);
413         }
414         return state;
415     }
416     
417     @Override
418     public void storeDynamicViewStructureMetadata(FacesContext context, UIViewRoot root,
419             FaceletState faceletDynamicState)
420     {
421         DynamicViewKey key = (DynamicViewKey) generateDynamicStructureViewKey(context, root, faceletDynamicState);
422         MetadataViewKey ordinaryKey = deriveViewKey(context, root);
423         if (!dynamicStructureViewMetadataMap.containsKey(ordinaryKey))
424         {
425             
426             Map<DynamicViewKey, ViewStructureMetadata> map = dynamicStructureViewMetadataMap.get(ordinaryKey);
427             if (map == null)
428             {
429                 map = new ConcurrentHashMap<DynamicViewKey, ViewStructureMetadata>();
430                 dynamicStructureViewMetadataMap.put(ordinaryKey, map);
431             }
432             RequestViewContext rvc = RequestViewContext.getCurrentInstance(context);
433             
434             Object state = saveViewRootState(context, root);
435 
436             ViewStructureMetadata metadata = new ViewStructureMetadataImpl(state, 
437                     rvc.getRequestViewMetadata().cloneInstance());
438             map.put(key, metadata);
439         }
440     }
441 
442     @Override
443     public ViewStructureMetadata retrieveDynamicViewStructureMetadata(FacesContext context, UIViewRoot root,
444             FaceletState  faceletDynamicState)
445     {
446         DynamicViewKey key = (DynamicViewKey) generateDynamicStructureViewKey(context, root, faceletDynamicState);
447         MetadataViewKey ordinaryKey = deriveViewKey(context, root);
448         Map<DynamicViewKey, ViewStructureMetadata> map = dynamicStructureViewMetadataMap.get(ordinaryKey);
449         if (map != null)
450         {
451             return map.get(key);
452         }
453         return null;
454     }
455 
456     /**
457      * @return the deferredNavigation
458      */
459     @Override
460     public boolean isDeferredNavigationEnabled()
461     {
462         return deferredNavigation;
463     }
464     
465 }