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.lifecycle;
20  
21  import java.net.MalformedURLException;
22  import java.util.Collections;
23  import java.util.EnumSet;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.FacesException;
30  import javax.faces.FactoryFinder;
31  import javax.faces.application.ProjectStage;
32  import javax.faces.application.ViewHandler;
33  import javax.faces.component.UIComponent;
34  import javax.faces.component.visit.VisitCallback;
35  import javax.faces.component.visit.VisitContext;
36  import javax.faces.component.visit.VisitContextFactory;
37  import javax.faces.component.visit.VisitHint;
38  import javax.faces.component.visit.VisitResult;
39  import javax.faces.context.ExternalContext;
40  import javax.faces.context.FacesContext;
41  import javax.faces.event.PostRestoreStateEvent;
42  import javax.faces.render.RenderKitFactory;
43  import javax.faces.render.ResponseStateManager;
44  import javax.faces.view.ViewDeclarationLanguage;
45  
46  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
47  import org.apache.myfaces.shared.application.FacesServletMapping;
48  import org.apache.myfaces.shared.application.InvalidViewIdException;
49  import org.apache.myfaces.shared.util.Assert;
50  import org.apache.myfaces.shared.util.ConcurrentLRUCache;
51  import org.apache.myfaces.shared.util.ExternalContextUtils;
52  import org.apache.myfaces.shared.util.WebConfigParamUtils;
53  
54  /**
55   * @author Mathias Broekelmann (latest modification by $Author$)
56   * @version $Revision$ $Date$
57   */
58  public class DefaultRestoreViewSupport implements RestoreViewSupport
59  {
60      private static final String JAVAX_SERVLET_INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
61  
62      private static final String JAVAX_SERVLET_INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
63      
64      /**
65       * Constant defined on javax.portlet.faces.Bridge class that helps to 
66       * define if the current request is a portlet request or not.
67       */
68      private static final String PORTLET_LIFECYCLE_PHASE = "javax.portlet.faces.phase";
69      
70      private static final String CACHED_SERVLET_MAPPING =
71          DefaultRestoreViewSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
72  
73  
74      //private final Log log = LogFactory.getLog(DefaultRestoreViewSupport.class);
75      private final Logger log = Logger.getLogger(DefaultRestoreViewSupport.class.getName());
76  
77      /**
78       * Controls the size of the cache used to "remember" if a view exists or not.
79       */
80      @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="viewhandler", tags="performance", 
81              classType="java.lang.Integer",
82              desc="Controls the size of the cache used to 'remember' if a view exists or not.")
83      private static final String CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.CHECKED_VIEWID_CACHE_SIZE";
84      private static final int CHECKED_VIEWID_CACHE_DEFAULT_SIZE = 500;
85  
86      /**
87       * Enable or disable a cache used to "remember" if a view exists or not and reduce the impact of
88       * sucesive calls to ExternalContext.getResource().
89       */
90      @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", expectedValues="true, false", group="viewhandler", 
91              tags="performance",
92              desc="Enable or disable a cache used to 'remember' if a view exists or not and reduce the impact " +
93                   "of sucesive calls to ExternalContext.getResource().")
94      private static final String CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE = 
95          "org.apache.myfaces.CHECKED_VIEWID_CACHE_ENABLED";
96      private static final boolean CHECKED_VIEWID_CACHE_ENABLED_DEFAULT = true;
97      
98      private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
99      
100     private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet( 
101             EnumSet.of(VisitHint.SKIP_ITERATION));
102 
103     private volatile ConcurrentLRUCache<String, Boolean> _checkedViewIdMap = null;
104     private Boolean _checkedViewIdCacheEnabled = null;
105     
106     private RenderKitFactory _renderKitFactory = null;
107     private VisitContextFactory _visitContextFactory = null;
108     
109     private final String[] _faceletsViewMappings;
110     private final String[] _contextSuffixes;
111     private final String _faceletsContextSufix;
112     private final boolean _initialized;
113     
114     public DefaultRestoreViewSupport()
115     {
116         _faceletsViewMappings = null;
117         _contextSuffixes = null;
118         _faceletsContextSufix = null;
119         _initialized = false;
120     }
121     
122     public DefaultRestoreViewSupport(FacesContext facesContext)
123     {
124         _faceletsViewMappings = getFaceletsViewMappings(facesContext);
125         _contextSuffixes = getContextSuffix(facesContext);
126         _faceletsContextSufix = getFaceletsContextSuffix(facesContext);
127         _initialized = true;
128     }
129 
130     public void processComponentBinding(FacesContext facesContext, UIComponent component)
131     {
132         // JSF 2.0: Old hack related to t:aliasBean was fixed defining a event that traverse
133         // whole tree and let components to override UIComponent.processEvent() method to include it.
134         
135         // Remove this hack SKIP_ITERATION_HINT and use VisitHints.SKIP_ITERATION in JSF 2.1 only
136         // is not possible, because jsf 2.0 API-based libraries can use the String
137         // hint, JSF21-based libraries can use both.
138         try
139         {
140             facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
141 
142             VisitContext visitContext = (VisitContext) getVisitContextFactory().
143                     getVisitContext(facesContext, null, VISIT_HINTS);
144             component.visitTree(visitContext, new RestoreStateCallback());
145         }
146         finally
147         {
148             // We must remove hint in finally, because an exception can break this phase,
149             // but lifecycle can continue, if custom exception handler swallows the exception
150             facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
151         }
152         
153         
154         /*
155         ValueExpression binding = component.getValueExpression("binding");
156         if (binding != null)
157         {
158             binding.setValue(facesContext.getELContext(), component);
159         }
160 
161         // This part is for make compatibility with t:aliasBean, because
162         // this components has its own code before and after binding is
163         // set for child components.
164         RestoreStateUtils.recursivelyHandleComponentReferencesAndSetValid(facesContext, component);
165 
166         // The required behavior for the spec is call recursively this method
167         // for walk the component tree.
168         // for (Iterator<UIComponent> iter = component.getFacetsAndChildren(); iter.hasNext();)
169         // {
170         // processComponentBinding(facesContext, iter.next());
171         // }
172          */
173     }
174 
175     public String calculateViewId(FacesContext facesContext)
176     {
177         Assert.notNull(facesContext);
178         ExternalContext externalContext = facesContext.getExternalContext();
179         Map<String, Object> requestMap = externalContext.getRequestMap();
180 
181         String viewId = null;
182         boolean traceEnabled = log.isLoggable(Level.FINEST);
183         
184         if (requestMap.containsKey(PORTLET_LIFECYCLE_PHASE))
185         {
186             viewId = (String) externalContext.getRequestPathInfo();
187         }
188         else
189         {
190             viewId = (String) requestMap.get(JAVAX_SERVLET_INCLUDE_PATH_INFO);
191             if (viewId != null)
192             {
193                 if (traceEnabled)
194                 {
195                     log.finest("Calculated viewId '" + viewId + "' from request param '"
196                                + JAVAX_SERVLET_INCLUDE_PATH_INFO + "'");
197                 }
198             }
199             else
200             {
201                 viewId = externalContext.getRequestPathInfo();
202                 if (viewId != null && traceEnabled)
203                 {
204                     log.finest("Calculated viewId '" + viewId + "' from request path info");
205                 }
206             }
207     
208             if (viewId == null)
209             {
210                 viewId = (String) requestMap.get(JAVAX_SERVLET_INCLUDE_SERVLET_PATH);
211                 if (viewId != null && traceEnabled)
212                 {
213                     log.finest("Calculated viewId '" + viewId + "' from request param '"
214                             + JAVAX_SERVLET_INCLUDE_SERVLET_PATH + "'");
215                 }
216             }
217         }
218         
219         if (viewId == null)
220         {
221             viewId = externalContext.getRequestServletPath();
222             if (viewId != null && traceEnabled)
223             {
224                 log.finest("Calculated viewId '" + viewId + "' from request servlet path");
225             }
226         }
227 
228         if (viewId == null)
229         {
230             throw new FacesException("Could not determine view id.");
231         }
232 
233         return viewId;
234     }
235 
236     public boolean isPostback(FacesContext facesContext)
237     {
238         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
239         String renderkitId = viewHandler.calculateRenderKitId(facesContext);
240         ResponseStateManager rsm
241                 = getRenderKitFactory().getRenderKit(facesContext, renderkitId).getResponseStateManager();
242         return rsm.isPostback(facesContext);
243     }
244     
245     protected RenderKitFactory getRenderKitFactory()
246     {
247         if (_renderKitFactory == null)
248         {
249             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
250         }
251         return _renderKitFactory;
252     }
253     
254     protected VisitContextFactory getVisitContextFactory()
255     {
256         if (_visitContextFactory == null)
257         {
258             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
259         }
260         return _visitContextFactory;
261     }
262         
263     private static class RestoreStateCallback implements VisitCallback
264     {
265         private PostRestoreStateEvent event;
266 
267         public VisitResult visit(VisitContext context, UIComponent target)
268         {
269             if (event == null)
270             {
271                 event = new PostRestoreStateEvent(target);
272             }
273             else
274             {
275                 event.setComponent(target);
276             }
277 
278             // call the processEvent method of the current component.
279             // The argument event must be an instance of AfterRestoreStateEvent whose component
280             // property is the current component in the traversal.
281             target.processEvent(event);
282             
283             return VisitResult.ACCEPT;
284         }
285     }
286     
287     @Deprecated
288     public String deriveViewId(FacesContext context, String viewId)
289     {
290         //If no viewId found, don't try to derive it, just continue.
291         if (viewId == null)
292         {
293             return null;
294         }
295         FacesServletMapping mapping = getFacesServletMapping(context);
296         if (mapping == null || mapping.isExtensionMapping())
297         {
298             viewId = handleSuffixMapping(context, viewId);
299         }
300         else if(mapping.isPrefixMapping())
301         {
302             viewId = handlePrefixMapping(viewId,mapping.getPrefix());
303             
304             // A viewId that is equals to the prefix mapping on servlet mode is
305             // considered invalid, because jsp vdl will use RequestDispatcher and cause
306             // a loop that ends in a exception. Note in portlet mode the view
307             // could be encoded as a query param, so the viewId could be valid.
308             if (viewId != null && viewId.equals(mapping.getPrefix()) &&
309                 !ExternalContextUtils.isPortlet(context.getExternalContext()))
310             {
311                 throw new InvalidViewIdException();
312             }
313         }
314         else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
315         {
316             throw new InvalidViewIdException(viewId);
317         }
318 
319         //if(viewId != null)
320         //{
321         //    return (checkResourceExists(context,viewId) ? viewId : null);
322         //}
323 
324         return viewId;    // return null if no physical resource exists
325     }
326     
327     protected String[] getContextSuffix(FacesContext context)
328     {
329         String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
330         if (defaultSuffix == null)
331         {
332             defaultSuffix = ViewHandler.DEFAULT_SUFFIX;
333         }
334         return defaultSuffix.split(" ");
335     }
336     
337     protected String getFaceletsContextSuffix(FacesContext context)
338     {
339         String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
340         if (defaultSuffix == null)
341         {
342             defaultSuffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
343         }
344         return defaultSuffix;
345     }
346     
347     
348     
349     protected String[] getFaceletsViewMappings(FacesContext context)
350     {
351         String faceletsViewMappings
352                 = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
353         if(faceletsViewMappings == null)    //consider alias facelets.VIEW_MAPPINGS
354         {
355             faceletsViewMappings= context.getExternalContext().getInitParameter("facelets.VIEW_MAPPINGS");
356         }
357         
358         return faceletsViewMappings == null ? null : faceletsViewMappings.split(";");
359     }
360     
361     /**
362      * Return the normalized viewId according to the algorithm specified in 7.5.2 
363      * by stripping off any number of occurrences of the prefix mapping from the viewId.
364      * <p/>
365      * For example, both /faces/view.xhtml and /faces/faces/faces/view.xhtml would both return view.xhtml
366      * F 
367      */
368     protected String handlePrefixMapping(String viewId, String prefix)
369     {
370         /*  If prefix mapping (such as "/faces/*") is used for FacesServlet,
371         normalize the viewId according to the following
372             algorithm, or its semantic equivalent, and return it.
373                
374             Remove any number of occurrences of the prefix mapping from the viewId. For example, if the incoming value
375             was /faces/faces/faces/view.xhtml the result would be simply view.xhtml.
376          */
377         String uri = viewId;
378         if ( "".equals(prefix) )
379         {
380             // if prefix is an empty string, we let it be "//"
381             // in order to prevent an infinite loop in uri.startsWith(-emptyString-).
382             // Furthermore a prefix of "//" is just another double slash prevention.
383             prefix = "//";
384         }
385         else
386         {
387             //need to make sure its really /faces/* and not /facesPage.xhtml
388             prefix = prefix + '/';  
389         }
390         while (uri.startsWith(prefix) || uri.startsWith("//")) 
391         {
392             if(uri.startsWith(prefix))
393             {
394                 //cut off only /faces, leave the trailing '/' char for the next iteration
395                 uri = uri.substring(prefix.length() - 1);
396             }
397             else //uri starts with '//'
398             {
399                 //cut off the leading slash, leaving the second slash to compare for the next iteration
400                 uri = uri.substring(1);
401             }
402         }
403         //now delete any remaining leading '/'
404         // TODO: CJH: I don't think this is correct, considering that getActionURL() expects everything to
405         // start with '/', and in the suffix case we only mess with the suffix and leave leading
406         // slashes alone.  Please review...
407         /*if(uri.startsWith("/"))
408         {
409             uri = uri.substring(1);
410         }*/
411         
412         return uri;
413     }
414     
415     /**
416      * Return the viewId with any non-standard suffix stripped off and replaced with
417      * the default suffix configured for the specified context.
418      * <p/>
419      * For example, an input parameter of "/foo.jsf" may return "/foo.jsp".
420      */
421     protected String handleSuffixMapping(FacesContext context, String requestViewId)
422     {
423         String[] faceletsViewMappings = _initialized ? _faceletsViewMappings : getFaceletsViewMappings(context);
424         String[] jspDefaultSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context);
425         
426         int slashPos = requestViewId.lastIndexOf('/');
427         int extensionPos = requestViewId.lastIndexOf('.');
428         
429         //Try to locate any resource that match with the expected id
430         for (String defaultSuffix : jspDefaultSuffixes)
431         {
432             StringBuilder builder = new StringBuilder(requestViewId);
433            
434             if (extensionPos > -1 && extensionPos > slashPos)
435             {
436                 builder.replace(extensionPos, requestViewId.length(), defaultSuffix);
437             }
438             else
439             {
440                 builder.append(defaultSuffix);
441             }
442             String candidateViewId = builder.toString();
443             
444             if( faceletsViewMappings != null && faceletsViewMappings.length > 0 )
445             {
446                 for (String mapping : faceletsViewMappings)
447                 {
448                     if(mapping.startsWith("/"))
449                     {
450                         continue;   //skip this entry, its a prefix mapping
451                     }
452                     if(mapping.equals(candidateViewId))
453                     {
454                         return candidateViewId;
455                     }
456                     if(mapping.startsWith(".")) //this is a wildcard entry
457                     {
458                         builder.setLength(0); //reset/reuse the builder object 
459                         builder.append(candidateViewId); 
460                         builder.replace(candidateViewId.lastIndexOf('.'), candidateViewId.length(), mapping);
461                         String tempViewId = builder.toString();
462                         if(checkResourceExists(context,tempViewId))
463                         {
464                             return tempViewId;
465                         }
466                     }
467                 }
468             }
469 
470             // forced facelets mappings did not match or there were no entries in faceletsViewMappings array
471             if(checkResourceExists(context,candidateViewId))
472             {
473                 return candidateViewId;
474             }
475         
476         }
477         
478         //jsp suffixes didn't match, try facelets suffix
479         String faceletsDefaultSuffix = _initialized ? _faceletsContextSufix : this.getFaceletsContextSuffix(context);
480         if (faceletsDefaultSuffix != null)
481         {
482             for (String defaultSuffix : jspDefaultSuffixes)
483             {
484                 if (faceletsDefaultSuffix.equals(defaultSuffix))
485                 {
486                     faceletsDefaultSuffix = null;
487                     break;
488                 }
489             }
490         }
491         if (faceletsDefaultSuffix != null)
492         {
493             StringBuilder builder = new StringBuilder(requestViewId);
494             
495             if (extensionPos > -1 && extensionPos > slashPos)
496             {
497                 builder.replace(extensionPos, requestViewId.length(), faceletsDefaultSuffix);
498             }
499             else
500             {
501                 builder.append(faceletsDefaultSuffix);
502             }
503             
504             String candidateViewId = builder.toString();
505             if(checkResourceExists(context,candidateViewId))
506             {
507                 return candidateViewId;
508             }
509         }
510 
511         // Otherwise, if a physical resource exists with the name requestViewId let that value be viewId.
512         if(checkResourceExists(context,requestViewId))
513         {
514             return requestViewId;
515         }
516         
517         //Otherwise return null.
518         return null;
519     }
520     protected boolean checkResourceExists(FacesContext context, String viewId)
521     {
522         try
523         {
524             if (isCheckedViewIdCachingEnabled(context))
525             {
526                 Boolean resourceExists = getCheckedViewIDMap(context).get(
527                         viewId);
528                 if (resourceExists == null)
529                 {
530                     ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
531                             .getViewDeclarationLanguage(context, viewId);
532                     if (vdl != null)
533                     {
534                         resourceExists = vdl.viewExists(context, viewId);
535                     }
536                     else
537                     {
538                         // Fallback to default strategy
539                         resourceExists = context.getExternalContext().getResource(
540                                 viewId) != null;
541                     }
542                     getCheckedViewIDMap(context).put(viewId, resourceExists);
543                 }
544                 return resourceExists;
545             }
546             else
547             {
548                 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
549                             .getViewDeclarationLanguage(context, viewId);
550                 if (vdl != null)
551                 {
552                     if (vdl.viewExists(context, viewId))
553                     {
554                         return true;
555                     }
556                 }
557                 else
558                 {
559                     // Fallback to default strategy
560                     if (context.getExternalContext().getResource(viewId) != null)
561                     {
562                         return true;
563                     }
564                 }
565             }
566         }
567         catch(MalformedURLException e)
568         {
569             //ignore and move on
570         }     
571         return false;
572     }
573 
574     public boolean checkViewExists(FacesContext facesContext, String viewId)
575     {
576         return checkResourceExists(facesContext, viewId);
577     }
578 
579     /**
580      * Read the web.xml file that is in the classpath and parse its internals to
581      * figure out how the FacesServlet is mapped for the current webapp.
582      */
583     protected FacesServletMapping getFacesServletMapping(FacesContext context)
584     {
585         Map<Object, Object> attributes = context.getAttributes();
586 
587         // Has the mapping already been determined during this request?
588         FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
589         if (mapping == null)
590         {
591             ExternalContext externalContext = context.getExternalContext();
592             mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
593                     externalContext.getRequestPathInfo());
594 
595             attributes.put(CACHED_SERVLET_MAPPING, mapping);
596         }
597         return mapping;
598     }
599 
600     /**
601      * Determines the mapping of the FacesServlet in the web.xml configuration
602      * file. However, there is no need to actually parse this configuration file
603      * as runtime information is sufficient.
604      *
605      * @param servletPath The servletPath of the current request
606      * @param pathInfo    The pathInfo of the current request
607      * @return the mapping of the FacesServlet in the web.xml configuration file
608      */
609     protected static FacesServletMapping calculateFacesServletMapping(
610         String servletPath, String pathInfo)
611     {
612         if (pathInfo != null)
613         {
614             // If there is a "extra path", it's definitely no extension mapping.
615             // Now we just have to determine the path which has been specified
616             // in the url-pattern, but that's easy as it's the same as the
617             // current servletPath. It doesn't even matter if "/*" has been used
618             // as in this case the servletPath is just an empty string according
619             // to the Servlet Specification (SRV 4.4).
620             return FacesServletMapping.createPrefixMapping(servletPath);
621         }
622         else
623         {
624             // In the case of extension mapping, no "extra path" is available.
625             // Still it's possible that prefix-based mapping has been used.
626             // Actually, if there was an exact match no "extra path"
627             // is available (e.g. if the url-pattern is "/faces/*"
628             // and the request-uri is "/context/faces").
629             int slashPos = servletPath.lastIndexOf('/');
630             int extensionPos = servletPath.lastIndexOf('.');
631             if (extensionPos > -1 && extensionPos > slashPos)
632             {
633                 String extension = servletPath.substring(extensionPos);
634                 return FacesServletMapping.createExtensionMapping(extension);
635             }
636             else
637             {
638                 // There is no extension in the given servletPath and therefore
639                 // we assume that it's an exact match using prefix-based mapping.
640                 return FacesServletMapping.createPrefixMapping(servletPath);
641             }
642         }
643     }
644     
645     private ConcurrentLRUCache<String, Boolean> getCheckedViewIDMap(FacesContext context)
646     {
647         if (_checkedViewIdMap == null)
648         {
649             int maxSize = getViewIDCacheMaxSize(context);
650             _checkedViewIdMap = new ConcurrentLRUCache<String, Boolean>((maxSize * 4 + 3) / 3, maxSize);
651         }
652         return _checkedViewIdMap;
653     }
654 
655     private boolean isCheckedViewIdCachingEnabled(FacesContext context)
656     {
657         if (_checkedViewIdCacheEnabled == null)
658         {
659             // first, check if the ProjectStage is development and skip caching in this case
660             if (context.isProjectStage(ProjectStage.Development))
661             {
662                 _checkedViewIdCacheEnabled = Boolean.FALSE;
663             }
664             else
665             {
666                 // in all ohter cases, make sure that the cache is not explicitly disabled via context param
667                 _checkedViewIdCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(),
668                         CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
669                         CHECKED_VIEWID_CACHE_ENABLED_DEFAULT);
670             }
671 
672             if (log.isLoggable(Level.FINE))
673             {
674                 log.log(Level.FINE, "MyFaces ViewID Caching Enabled="
675                         + _checkedViewIdCacheEnabled);
676             }
677         }
678         return _checkedViewIdCacheEnabled;
679     }
680 
681     private int getViewIDCacheMaxSize(FacesContext context)
682     {
683         ExternalContext externalContext = context.getExternalContext();
684 
685         return WebConfigParamUtils.getIntegerInitParameter(externalContext,
686                 CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE, CHECKED_VIEWID_CACHE_DEFAULT_SIZE);
687     }
688 }