Coverage Report - org.apache.myfaces.lifecycle.DefaultRestoreViewSupport
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultRestoreViewSupport
0%
0/182
0%
0/136
4.952
DefaultRestoreViewSupport$1
N/A
N/A
4.952
DefaultRestoreViewSupport$RestoreStateCallback
0%
0/6
0%
0/2
4.952
 
 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  0
     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  0
     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  0
     private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet( 
 101  
             EnumSet.of(VisitHint.SKIP_ITERATION));
 102  
 
 103  0
     private volatile ConcurrentLRUCache<String, Boolean> _checkedViewIdMap = null;
 104  0
     private Boolean _checkedViewIdCacheEnabled = null;
 105  
     
 106  0
     private RenderKitFactory _renderKitFactory = null;
 107  0
     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  0
     {
 116  0
         _faceletsViewMappings = null;
 117  0
         _contextSuffixes = null;
 118  0
         _faceletsContextSufix = null;
 119  0
         _initialized = false;
 120  0
     }
 121  
     
 122  
     public DefaultRestoreViewSupport(FacesContext facesContext)
 123  0
     {
 124  0
         _faceletsViewMappings = getFaceletsViewMappings(facesContext);
 125  0
         _contextSuffixes = getContextSuffix(facesContext);
 126  0
         _faceletsContextSufix = getFaceletsContextSuffix(facesContext);
 127  0
         _initialized = true;
 128  0
     }
 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  0
             facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
 141  
 
 142  0
             VisitContext visitContext = (VisitContext) getVisitContextFactory().
 143  
                     getVisitContext(facesContext, null, VISIT_HINTS);
 144  0
             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  0
             facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
 151  0
         }
 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  0
     }
 174  
 
 175  
     public String calculateViewId(FacesContext facesContext)
 176  
     {
 177  0
         Assert.notNull(facesContext);
 178  0
         ExternalContext externalContext = facesContext.getExternalContext();
 179  0
         Map<String, Object> requestMap = externalContext.getRequestMap();
 180  
 
 181  0
         String viewId = null;
 182  0
         boolean traceEnabled = log.isLoggable(Level.FINEST);
 183  
         
 184  0
         if (requestMap.containsKey(PORTLET_LIFECYCLE_PHASE))
 185  
         {
 186  0
             viewId = (String) externalContext.getRequestPathInfo();
 187  
         }
 188  
         else
 189  
         {
 190  0
             viewId = (String) requestMap.get(JAVAX_SERVLET_INCLUDE_PATH_INFO);
 191  0
             if (viewId != null)
 192  
             {
 193  0
                 if (traceEnabled)
 194  
                 {
 195  0
                     log.finest("Calculated viewId '" + viewId + "' from request param '"
 196  
                                + JAVAX_SERVLET_INCLUDE_PATH_INFO + "'");
 197  
                 }
 198  
             }
 199  
             else
 200  
             {
 201  0
                 viewId = externalContext.getRequestPathInfo();
 202  0
                 if (viewId != null && traceEnabled)
 203  
                 {
 204  0
                     log.finest("Calculated viewId '" + viewId + "' from request path info");
 205  
                 }
 206  
             }
 207  
     
 208  0
             if (viewId == null)
 209  
             {
 210  0
                 viewId = (String) requestMap.get(JAVAX_SERVLET_INCLUDE_SERVLET_PATH);
 211  0
                 if (viewId != null && traceEnabled)
 212  
                 {
 213  0
                     log.finest("Calculated viewId '" + viewId + "' from request param '"
 214  
                             + JAVAX_SERVLET_INCLUDE_SERVLET_PATH + "'");
 215  
                 }
 216  
             }
 217  
         }
 218  
         
 219  0
         if (viewId == null)
 220  
         {
 221  0
             viewId = externalContext.getRequestServletPath();
 222  0
             if (viewId != null && traceEnabled)
 223  
             {
 224  0
                 log.finest("Calculated viewId '" + viewId + "' from request servlet path");
 225  
             }
 226  
         }
 227  
 
 228  0
         if (viewId == null)
 229  
         {
 230  0
             throw new FacesException("Could not determine view id.");
 231  
         }
 232  
 
 233  0
         return viewId;
 234  
     }
 235  
 
 236  
     public boolean isPostback(FacesContext facesContext)
 237  
     {
 238  0
         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
 239  0
         String renderkitId = viewHandler.calculateRenderKitId(facesContext);
 240  0
         ResponseStateManager rsm
 241  
                 = getRenderKitFactory().getRenderKit(facesContext, renderkitId).getResponseStateManager();
 242  0
         return rsm.isPostback(facesContext);
 243  
     }
 244  
     
 245  
     protected RenderKitFactory getRenderKitFactory()
 246  
     {
 247  0
         if (_renderKitFactory == null)
 248  
         {
 249  0
             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
 250  
         }
 251  0
         return _renderKitFactory;
 252  
     }
 253  
     
 254  
     protected VisitContextFactory getVisitContextFactory()
 255  
     {
 256  0
         if (_visitContextFactory == null)
 257  
         {
 258  0
             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
 259  
         }
 260  0
         return _visitContextFactory;
 261  
     }
 262  
         
 263  0
     private static class RestoreStateCallback implements VisitCallback
 264  
     {
 265  
         private PostRestoreStateEvent event;
 266  
 
 267  
         public VisitResult visit(VisitContext context, UIComponent target)
 268  
         {
 269  0
             if (event == null)
 270  
             {
 271  0
                 event = new PostRestoreStateEvent(target);
 272  
             }
 273  
             else
 274  
             {
 275  0
                 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  0
             target.processEvent(event);
 282  
             
 283  0
             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  0
         if (viewId == null)
 292  
         {
 293  0
             return null;
 294  
         }
 295  0
         FacesServletMapping mapping = getFacesServletMapping(context);
 296  0
         if (mapping == null || mapping.isExtensionMapping())
 297  
         {
 298  0
             viewId = handleSuffixMapping(context, viewId);
 299  
         }
 300  0
         else if(mapping.isPrefixMapping())
 301  
         {
 302  0
             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  0
             if (viewId != null && viewId.equals(mapping.getPrefix()) &&
 309  
                 !ExternalContextUtils.isPortlet(context.getExternalContext()))
 310  
             {
 311  0
                 throw new InvalidViewIdException();
 312  
             }
 313  
         }
 314  0
         else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
 315  
         {
 316  0
             throw new InvalidViewIdException(viewId);
 317  
         }
 318  
 
 319  
         //if(viewId != null)
 320  
         //{
 321  
         //    return (checkResourceExists(context,viewId) ? viewId : null);
 322  
         //}
 323  
 
 324  0
         return viewId;    // return null if no physical resource exists
 325  
     }
 326  
     
 327  
     protected String[] getContextSuffix(FacesContext context)
 328  
     {
 329  0
         String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
 330  0
         if (defaultSuffix == null)
 331  
         {
 332  0
             defaultSuffix = ViewHandler.DEFAULT_SUFFIX;
 333  
         }
 334  0
         return defaultSuffix.split(" ");
 335  
     }
 336  
     
 337  
     protected String getFaceletsContextSuffix(FacesContext context)
 338  
     {
 339  0
         String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
 340  0
         if (defaultSuffix == null)
 341  
         {
 342  0
             defaultSuffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
 343  
         }
 344  0
         return defaultSuffix;
 345  
     }
 346  
     
 347  
     
 348  
     
 349  
     protected String[] getFaceletsViewMappings(FacesContext context)
 350  
     {
 351  0
         String faceletsViewMappings
 352  
                 = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
 353  0
         if(faceletsViewMappings == null)    //consider alias facelets.VIEW_MAPPINGS
 354  
         {
 355  0
             faceletsViewMappings= context.getExternalContext().getInitParameter("facelets.VIEW_MAPPINGS");
 356  
         }
 357  
         
 358  0
         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  0
         String uri = viewId;
 378  0
         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  0
             prefix = "//";
 384  
         }
 385  
         else
 386  
         {
 387  
             //need to make sure its really /faces/* and not /facesPage.xhtml
 388  0
             prefix = prefix + '/';  
 389  
         }
 390  0
         while (uri.startsWith(prefix) || uri.startsWith("//")) 
 391  
         {
 392  0
             if(uri.startsWith(prefix))
 393  
             {
 394  
                 //cut off only /faces, leave the trailing '/' char for the next iteration
 395  0
                 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  0
                 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  0
         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  0
         String[] faceletsViewMappings = _initialized ? _faceletsViewMappings : getFaceletsViewMappings(context);
 424  0
         String[] jspDefaultSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context);
 425  
         
 426  0
         int slashPos = requestViewId.lastIndexOf('/');
 427  0
         int extensionPos = requestViewId.lastIndexOf('.');
 428  
         
 429  
         //Try to locate any resource that match with the expected id
 430  0
         for (String defaultSuffix : jspDefaultSuffixes)
 431  
         {
 432  0
             StringBuilder builder = new StringBuilder(requestViewId);
 433  
            
 434  0
             if (extensionPos > -1 && extensionPos > slashPos)
 435  
             {
 436  0
                 builder.replace(extensionPos, requestViewId.length(), defaultSuffix);
 437  
             }
 438  
             else
 439  
             {
 440  0
                 builder.append(defaultSuffix);
 441  
             }
 442  0
             String candidateViewId = builder.toString();
 443  
             
 444  0
             if( faceletsViewMappings != null && faceletsViewMappings.length > 0 )
 445  
             {
 446  0
                 for (String mapping : faceletsViewMappings)
 447  
                 {
 448  0
                     if(mapping.startsWith("/"))
 449  
                     {
 450  0
                         continue;   //skip this entry, its a prefix mapping
 451  
                     }
 452  0
                     if(mapping.equals(candidateViewId))
 453  
                     {
 454  0
                         return candidateViewId;
 455  
                     }
 456  0
                     if(mapping.startsWith(".")) //this is a wildcard entry
 457  
                     {
 458  0
                         builder.setLength(0); //reset/reuse the builder object 
 459  0
                         builder.append(candidateViewId); 
 460  0
                         builder.replace(candidateViewId.lastIndexOf('.'), candidateViewId.length(), mapping);
 461  0
                         String tempViewId = builder.toString();
 462  0
                         if(checkResourceExists(context,tempViewId))
 463  
                         {
 464  0
                             return tempViewId;
 465  
                         }
 466  
                     }
 467  
                 }
 468  
             }
 469  
 
 470  
             // forced facelets mappings did not match or there were no entries in faceletsViewMappings array
 471  0
             if(checkResourceExists(context,candidateViewId))
 472  
             {
 473  0
                 return candidateViewId;
 474  
             }
 475  
         
 476  
         }
 477  
         
 478  
         //jsp suffixes didn't match, try facelets suffix
 479  0
         String faceletsDefaultSuffix = _initialized ? _faceletsContextSufix : this.getFaceletsContextSuffix(context);
 480  0
         if (faceletsDefaultSuffix != null)
 481  
         {
 482  0
             for (String defaultSuffix : jspDefaultSuffixes)
 483  
             {
 484  0
                 if (faceletsDefaultSuffix.equals(defaultSuffix))
 485  
                 {
 486  0
                     faceletsDefaultSuffix = null;
 487  0
                     break;
 488  
                 }
 489  
             }
 490  
         }
 491  0
         if (faceletsDefaultSuffix != null)
 492  
         {
 493  0
             StringBuilder builder = new StringBuilder(requestViewId);
 494  
             
 495  0
             if (extensionPos > -1 && extensionPos > slashPos)
 496  
             {
 497  0
                 builder.replace(extensionPos, requestViewId.length(), faceletsDefaultSuffix);
 498  
             }
 499  
             else
 500  
             {
 501  0
                 builder.append(faceletsDefaultSuffix);
 502  
             }
 503  
             
 504  0
             String candidateViewId = builder.toString();
 505  0
             if(checkResourceExists(context,candidateViewId))
 506  
             {
 507  0
                 return candidateViewId;
 508  
             }
 509  
         }
 510  
 
 511  
         // Otherwise, if a physical resource exists with the name requestViewId let that value be viewId.
 512  0
         if(checkResourceExists(context,requestViewId))
 513  
         {
 514  0
             return requestViewId;
 515  
         }
 516  
         
 517  
         //Otherwise return null.
 518  0
         return null;
 519  
     }
 520  
     protected boolean checkResourceExists(FacesContext context, String viewId)
 521  
     {
 522  
         try
 523  
         {
 524  0
             if (isCheckedViewIdCachingEnabled(context))
 525  
             {
 526  0
                 Boolean resourceExists = getCheckedViewIDMap(context).get(
 527  
                         viewId);
 528  0
                 if (resourceExists == null)
 529  
                 {
 530  0
                     ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
 531  
                             .getViewDeclarationLanguage(context, viewId);
 532  0
                     if (vdl != null)
 533  
                     {
 534  0
                         resourceExists = vdl.viewExists(context, viewId);
 535  
                     }
 536  
                     else
 537  
                     {
 538  
                         // Fallback to default strategy
 539  0
                         resourceExists = context.getExternalContext().getResource(
 540  
                                 viewId) != null;
 541  
                     }
 542  0
                     getCheckedViewIDMap(context).put(viewId, resourceExists);
 543  
                 }
 544  0
                 return resourceExists;
 545  
             }
 546  
             else
 547  
             {
 548  0
                 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
 549  
                             .getViewDeclarationLanguage(context, viewId);
 550  0
                 if (vdl != null)
 551  
                 {
 552  0
                     if (vdl.viewExists(context, viewId))
 553  
                     {
 554  0
                         return true;
 555  
                     }
 556  
                 }
 557  
                 else
 558  
                 {
 559  
                     // Fallback to default strategy
 560  0
                     if (context.getExternalContext().getResource(viewId) != null)
 561  
                     {
 562  0
                         return true;
 563  
                     }
 564  
                 }
 565  
             }
 566  
         }
 567  0
         catch(MalformedURLException e)
 568  
         {
 569  
             //ignore and move on
 570  0
         }     
 571  0
         return false;
 572  
     }
 573  
 
 574  
     public boolean checkViewExists(FacesContext facesContext, String viewId)
 575  
     {
 576  0
         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  0
         Map<Object, Object> attributes = context.getAttributes();
 586  
 
 587  
         // Has the mapping already been determined during this request?
 588  0
         FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
 589  0
         if (mapping == null)
 590  
         {
 591  0
             ExternalContext externalContext = context.getExternalContext();
 592  0
             mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
 593  
                     externalContext.getRequestPathInfo());
 594  
 
 595  0
             attributes.put(CACHED_SERVLET_MAPPING, mapping);
 596  
         }
 597  0
         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  0
         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  0
             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  0
             int slashPos = servletPath.lastIndexOf('/');
 630  0
             int extensionPos = servletPath.lastIndexOf('.');
 631  0
             if (extensionPos > -1 && extensionPos > slashPos)
 632  
             {
 633  0
                 String extension = servletPath.substring(extensionPos);
 634  0
                 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  0
                 return FacesServletMapping.createPrefixMapping(servletPath);
 641  
             }
 642  
         }
 643  
     }
 644  
     
 645  
     private ConcurrentLRUCache<String, Boolean> getCheckedViewIDMap(FacesContext context)
 646  
     {
 647  0
         if (_checkedViewIdMap == null)
 648  
         {
 649  0
             int maxSize = getViewIDCacheMaxSize(context);
 650  0
             _checkedViewIdMap = new ConcurrentLRUCache<String, Boolean>((maxSize * 4 + 3) / 3, maxSize);
 651  
         }
 652  0
         return _checkedViewIdMap;
 653  
     }
 654  
 
 655  
     private boolean isCheckedViewIdCachingEnabled(FacesContext context)
 656  
     {
 657  0
         if (_checkedViewIdCacheEnabled == null)
 658  
         {
 659  
             // first, check if the ProjectStage is development and skip caching in this case
 660  0
             if (context.isProjectStage(ProjectStage.Development))
 661  
             {
 662  0
                 _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  0
                 _checkedViewIdCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(),
 668  
                         CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
 669  
                         CHECKED_VIEWID_CACHE_ENABLED_DEFAULT);
 670  
             }
 671  
 
 672  0
             if (log.isLoggable(Level.FINE))
 673  
             {
 674  0
                 log.log(Level.FINE, "MyFaces ViewID Caching Enabled="
 675  
                         + _checkedViewIdCacheEnabled);
 676  
             }
 677  
         }
 678  0
         return _checkedViewIdCacheEnabled;
 679  
     }
 680  
 
 681  
     private int getViewIDCacheMaxSize(FacesContext context)
 682  
     {
 683  0
         ExternalContext externalContext = context.getExternalContext();
 684  
 
 685  0
         return WebConfigParamUtils.getIntegerInitParameter(externalContext,
 686  
                 CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE, CHECKED_VIEWID_CACHE_DEFAULT_SIZE);
 687  
     }
 688  
 }