You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2018/12/04 09:00:28 UTC

[myfaces] branch 2.3.x updated: MYFACES-4270

This is an automated email from the ASF dual-hosted git repository.

tandraschko pushed a commit to branch 2.3.x
in repository https://gitbox.apache.org/repos/asf/myfaces.git


The following commit(s) were added to refs/heads/2.3.x by this push:
     new 41394d7  MYFACES-4270
41394d7 is described below

commit 41394d78f60ed725032e5dbef00ef05d4aeb3ff6
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Tue Dec 4 10:00:22 2018 +0100

    MYFACES-4270
---
 .../lifecycle/DefaultRestoreViewSupport.java       |  58 ++--
 .../shared/application/CheckedViewIdsCache.java    | 111 +++++++
 .../application/DefaultViewHandlerSupport.java     | 322 +++++++--------------
 .../shared/application/FacesServletMapping.java    |  27 +-
 .../application/FacesServletMappingUtils.java      |  21 +-
 5 files changed, 261 insertions(+), 278 deletions(-)

diff --git a/impl/src/main/java/org/apache/myfaces/lifecycle/DefaultRestoreViewSupport.java b/impl/src/main/java/org/apache/myfaces/lifecycle/DefaultRestoreViewSupport.java
index 0ab9ac9..eda42af 100644
--- a/impl/src/main/java/org/apache/myfaces/lifecycle/DefaultRestoreViewSupport.java
+++ b/impl/src/main/java/org/apache/myfaces/lifecycle/DefaultRestoreViewSupport.java
@@ -50,6 +50,7 @@ import org.apache.myfaces.shared.util.Assert;
 import org.apache.myfaces.shared.util.ConcurrentLRUCache;
 import org.apache.myfaces.shared.util.ExternalContextUtils;
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
+import org.apache.myfaces.shared.application.CheckedViewIdsCache;
 
 /**
  * @author Mathias Broekelmann (latest modification by $Author$)
@@ -110,6 +111,7 @@ public class DefaultRestoreViewSupport implements RestoreViewSupport
     private final String[] _contextSuffixes;
     private final String _faceletsContextSufix;
     private final boolean _initialized;
+    private CheckedViewIdsCache checkedViewIdsCache = null;
     
     public DefaultRestoreViewSupport()
     {
@@ -518,54 +520,44 @@ public class DefaultRestoreViewSupport implements RestoreViewSupport
         //Otherwise return null.
         return null;
     }
-    protected boolean checkResourceExists(FacesContext context, String viewId)
+    protected boolean checkResourceExists(FacesContext facesContext, String viewId)
     {
+        if (checkedViewIdsCache == null)
+        {
+            checkedViewIdsCache = CheckedViewIdsCache.getInstance(facesContext);
+        }
+        
         try
         {
-            if (isCheckedViewIdCachingEnabled(context))
+            Boolean resourceExists = null;
+            if (checkedViewIdsCache.isEnabled())
             {
-                Boolean resourceExists = getCheckedViewIDMap(context).get(
-                        viewId);
-                if (resourceExists == null)
-                {
-                    ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
-                            .getViewDeclarationLanguage(context, viewId);
-                    if (vdl != null)
-                    {
-                        resourceExists = vdl.viewExists(context, viewId);
-                    }
-                    else
-                    {
-                        // Fallback to default strategy
-                        resourceExists = context.getExternalContext().getResource(
-                                viewId) != null;
-                    }
-                    getCheckedViewIDMap(context).put(viewId, resourceExists);
-                }
-                return resourceExists;
+                resourceExists = checkedViewIdsCache.getCache().get(viewId);
             }
-            else
+
+            if (resourceExists == null)
             {
-                ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
-                            .getViewDeclarationLanguage(context, viewId);
+                ViewDeclarationLanguage vdl = facesContext.getApplication().getViewHandler()
+                        .getViewDeclarationLanguage(facesContext, viewId);
                 if (vdl != null)
                 {
-                    if (vdl.viewExists(context, viewId))
-                    {
-                        return true;
-                    }
+                    resourceExists = vdl.viewExists(facesContext, viewId);
                 }
                 else
                 {
                     // Fallback to default strategy
-                    if (context.getExternalContext().getResource(viewId) != null)
-                    {
-                        return true;
-                    }
+                    resourceExists = facesContext.getExternalContext().getResource(viewId) != null;
+                }
+
+                if (checkedViewIdsCache.isEnabled())
+                {
+                    checkedViewIdsCache.getCache().put(viewId, resourceExists);
                 }
             }
+
+            return resourceExists;
         }
-        catch(MalformedURLException e)
+        catch (MalformedURLException e)
         {
             //ignore and move on
         }     
diff --git a/shared/src/main/java/org/apache/myfaces/shared/application/CheckedViewIdsCache.java b/shared/src/main/java/org/apache/myfaces/shared/application/CheckedViewIdsCache.java
new file mode 100644
index 0000000..64f8a16
--- /dev/null
+++ b/shared/src/main/java/org/apache/myfaces/shared/application/CheckedViewIdsCache.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.shared.application;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.faces.application.ProjectStage;
+import javax.faces.context.FacesContext;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
+import org.apache.myfaces.shared.util.ConcurrentLRUCache;
+import org.apache.myfaces.shared.util.WebConfigParamUtils;
+
+public class CheckedViewIdsCache
+{
+    private static final Logger LOG = Logger.getLogger(CheckedViewIdsCache.class.getName());
+    private static final String INSTANCE_KEY = CheckedViewIdsCache.class.getName();
+    
+    /**
+     * Controls the size of the cache used to "remember" if a view exists or not.
+     */
+    @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="viewhandler", tags="performance", 
+            classType="java.lang.Integer",
+            desc="Controls the size of the cache used to 'remember' if a view exists or not.")
+    private static final String CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.CHECKED_VIEWID_CACHE_SIZE";
+
+    /**
+     * Enable or disable a cache used to "remember" if a view exists or not and reduce the impact of
+     * sucesive calls to ExternalContext.getResource().
+     */
+    @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", expectedValues="true, false", group="viewhandler", 
+            tags="performance",
+            desc="Enable or disable a cache used to 'remember' if a view exists or not and reduce the impact " +
+                 "of sucesive calls to ExternalContext.getResource().")
+    private static final String CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE = 
+        "org.apache.myfaces.CHECKED_VIEWID_CACHE_ENABLED";
+
+    private volatile ConcurrentLRUCache<String, Boolean> cache = null;
+    private boolean enabled;
+    private int size;
+    
+    private CheckedViewIdsCache()
+    {
+    }
+    
+    public void init(FacesContext facesContext)
+    {
+        // first, check if the ProjectStage is development and skip caching in this case
+        if (facesContext.isProjectStage(ProjectStage.Development))
+        {
+            enabled = false;
+        }
+        else
+        {
+            // in all ohter cases, make sure that the cache is not explicitly disabled via context param
+            enabled = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
+                    CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
+                    true);
+        }
+        
+        size = WebConfigParamUtils.getIntegerInitParameter(facesContext.getExternalContext(),
+                CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE,
+                500);
+
+        cache = new ConcurrentLRUCache<>((size * 4 + 3) / 3, size);
+        
+        if (LOG.isLoggable(Level.FINE))
+        {
+            LOG.log(Level.FINE, "MyFaces CheckedViewIdsCache enabled=" + enabled + ", size=" + size);
+        }
+    }
+    
+    public boolean isEnabled()
+    {
+        return enabled;
+    }
+    
+    public ConcurrentLRUCache<String, Boolean> getCache()
+    {
+        return cache;
+    }
+    
+    public static CheckedViewIdsCache getInstance(FacesContext facesContext)
+    {
+        CheckedViewIdsCache instance = (CheckedViewIdsCache)
+                facesContext.getExternalContext().getApplicationMap().get(INSTANCE_KEY);
+        if (instance == null)
+        {
+            instance = new CheckedViewIdsCache();
+            instance.init(facesContext);
+            facesContext.getExternalContext().getApplicationMap().put(INSTANCE_KEY, instance);
+        }
+        
+        return instance;
+    }
+}
diff --git a/shared/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java b/shared/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java
index 1aa5427..aba4cfa 100644
--- a/shared/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java
+++ b/shared/src/main/java/org/apache/myfaces/shared/application/DefaultViewHandlerSupport.java
@@ -23,19 +23,16 @@ import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.faces.application.ProjectStage;
 import javax.faces.application.ViewHandler;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
 import javax.faces.render.ResponseStateManager;
 import javax.faces.view.ViewDeclarationLanguage;
 
-import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
-import org.apache.myfaces.shared.util.ConcurrentLRUCache;
+import org.apache.myfaces.shared.util.ExternalContextUtils;
 import org.apache.myfaces.shared.util.StringUtils;
 import org.apache.myfaces.shared.util.ViewProtectionUtils;
-import org.apache.myfaces.shared.util.WebConfigParamUtils;
 
 /**
  * A ViewHandlerSupport implementation for use with standard Java Servlet engines,
@@ -51,37 +48,14 @@ public class DefaultViewHandlerSupport implements ViewHandlerSupport
 
     //private static final Log log = LogFactory.getLog(DefaultViewHandlerSupport.class);
     private static final Logger log = Logger.getLogger(DefaultViewHandlerSupport.class.getName());
-
-    /**
-     * Controls the size of the cache used to "remember" if a view exists or not.
-     */
-    @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="viewhandler", tags="performance", 
-            classType="java.lang.Integer",
-            desc="Controls the size of the cache used to 'remember' if a view exists or not.")
-    private static final String CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.CHECKED_VIEWID_CACHE_SIZE";
-    private static final int CHECKED_VIEWID_CACHE_DEFAULT_SIZE = 500;
-
-    /**
-     * Enable or disable a cache used to "remember" if a view exists or not and reduce the impact of
-     * sucesive calls to ExternalContext.getResource().
-     */
-    @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", expectedValues="true, false", group="viewhandler", 
-            tags="performance",
-            desc="Enable or disable a cache used to 'remember' if a view exists or not and reduce the impact " +
-                 "of sucesive calls to ExternalContext.getResource().")
-    private static final String CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE = 
-        "org.apache.myfaces.CHECKED_VIEWID_CACHE_ENABLED";
-    private static final boolean CHECKED_VIEWID_CACHE_ENABLED_DEFAULT = true;
     
     private static final String VIEW_HANDLER_SUPPORT_SB = "oam.viewhandler.SUPPORT_SB";
-
-    private volatile ConcurrentLRUCache<String, Boolean> _checkedViewIdMap = null;
-    private Boolean _checkedViewIdCacheEnabled = null;
     
     private final String[] _faceletsViewMappings;
     private final String[] _contextSuffixes;
     private final String _faceletsContextSufix;
     private final boolean _initialized;
+    private CheckedViewIdsCache checkedViewIdsCache = null;
     
     public DefaultViewHandlerSupport()
     {
@@ -119,11 +93,11 @@ public class DefaultViewHandlerSupport implements ViewHandlerSupport
             // considered invalid, because jsp vdl will use RequestDispatcher and cause
             // a loop that ends in a exception. Note in portlet mode the view
             // could be encoded as a query param, so the viewId could be valid.
-            //if (viewId != null && viewId.equals(mapping.getPrefix()) &&
-            //    !ExternalContextUtils.isPortlet(context.getExternalContext()))
-            //{
-            //    throw new InvalidViewIdException();
-            //}
+            if (viewId != null && viewId.equals(mapping.getPrefix()) &&
+                !ExternalContextUtils.isPortlet(context.getExternalContext()))
+            {
+                throw new InvalidViewIdException();
+            }
             
             // In JSF 2.3 some changes were done in the VDL to avoid the jsp vdl
             // RequestDispatcher redirection (only accept viewIds with jsp extension).
@@ -222,94 +196,93 @@ public class DefaultViewHandlerSupport implements ViewHandlerSupport
         // In JSF 2.3 we could have cases where the viewId can be bound to an url-pattern that is not
         // prefix or suffix, but exact mapping. In this part we need to take the viewId and check if
         // the viewId is bound or not with a mapping.
-        String prefixedExactMappingViewId = calculatePrefixedExactMapping(context, viewId);
-        boolean prefixedExactMappingFound = false;
-        if (prefixedExactMappingViewId != null && prefixedExactMappingViewId.length() > 0)
+        if (mapping != null && mapping.isExactMapping())
         {
-            FacesServletMapping exactMapping = FacesServletMappingUtils.getExactMapping(
-                    context, prefixedExactMappingViewId);
-            if (exactMapping != null)
+            String exactMappingViewId = calculatePrefixedExactMapping(context, viewId);
+            if (exactMappingViewId != null && !exactMappingViewId.isEmpty())
             {
-                mapping = exactMapping;
-                prefixedExactMappingFound = true;
+                // if the current exactMapping already matches the requested viewId -> same view, skip....
+                if (!mapping.getExact().equals(exactMappingViewId))
+                {
+                    // different viewId -> lets try to lookup a exact mapping
+                    mapping = FacesServletMappingUtils.getExactMapping(context, exactMappingViewId);
+
+                    // no exactMapping for the requested viewId available BUT the current view is a exactMapping
+                    // we need a to search for a prefix or extension mapping
+                    if (mapping == null)
+                    {
+                        mapping = FacesServletMappingUtils.getGenericPrefixOrSuffixMapping(context);
+                        if (mapping == null)
+                        {
+                            throw new IllegalStateException(
+                                    "No generic (either prefix or suffix) servlet-mapping found for FacesServlet."
+                                    + "This is required serve views, that are not exact mapped.");
+                        }
+                    }
+                }
             }
         }
-        if (prefixedExactMappingFound)
-        {
-            builder.append(mapping.getPrefix());
-        }
-        else
+        
+        if (mapping != null)
         {
-            if (mapping != null)
+            if (mapping.isExactMapping())
+            {
+                builder.append(mapping.getExact());
+            }
+            else if (mapping.isExtensionMapping())
             {
-                if (mapping.isExact())
+                //See JSF 2.0 section 7.5.2 
+                String[] contextSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context); 
+                boolean founded = false;
+                for (String contextSuffix : contextSuffixes)
                 {
-                    // it means that the currentView is a exact mapping
-                    // but the view to resolve is not exact - we must fallback to a prefix or suffix mapping
-                    mapping = FacesServletMappingUtils.getPrefixOrSuffixMapping(context, viewId);
-                    if (mapping == null)
+                    if (viewId.endsWith(contextSuffix))
                     {
-                        throw new IllegalStateException(
-                                "No generic (either prefix or suffix) servlet-mapping found for FacesServlet."
-                                + "This is required serve views, that are not exact mapped.");
+                        builder.append(viewId.substring(0, viewId.indexOf(contextSuffix)));
+                        builder.append(mapping.getExtension());
+                        founded = true;
+                        break;
                     }
                 }
-
-                if (mapping.isExtensionMapping())
-                {
-                    //See JSF 2.0 section 7.5.2 
-                    String[] contextSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context); 
-                    boolean founded = false;
-                    for (String contextSuffix : contextSuffixes)
+                if (!founded)
+                {   
+                    //See JSF 2.0 section 7.5.2
+                    // - If the argument viewId has an extension, and this extension is mapping, 
+                    // the result is contextPath + viewId
+                    //
+                    // -= Leonardo Uribe =- It is evident that when the page is generated, the derived 
+                    // viewId will end with the 
+                    // right contextSuffix, and a navigation entry on faces-config.xml should use such id,
+                    // this is just a workaroud
+                    // for usability. There is a potential risk that change the mapping in a webapp make 
+                    // the same application fail,
+                    // so use viewIds ending with mapping extensions is not a good practice.
+                    if (viewId.endsWith(mapping.getExtension()))
                     {
-                        if (viewId.endsWith(contextSuffix))
-                        {
-                            builder.append(viewId.substring(0, viewId.indexOf(contextSuffix)));
-                            builder.append(mapping.getExtension());
-                            founded = true;
-                            break;
-                        }
+                        builder.append(viewId);
                     }
-                    if (!founded)
-                    {   
-                        //See JSF 2.0 section 7.5.2
-                        // - If the argument viewId has an extension, and this extension is mapping, 
-                        // the result is contextPath + viewId
-                        //
-                        // -= Leonardo Uribe =- It is evident that when the page is generated, the derived 
-                        // viewId will end with the 
-                        // right contextSuffix, and a navigation entry on faces-config.xml should use such id,
-                        // this is just a workaroud
-                        // for usability. There is a potential risk that change the mapping in a webapp make 
-                        // the same application fail,
-                        // so use viewIds ending with mapping extensions is not a good practice.
-                        if (viewId.endsWith(mapping.getExtension()))
-                        {
-                            builder.append(viewId);
-                        }
-                        else if(viewId.lastIndexOf('.') != -1 )
-                        {
-                            builder.append(viewId.substring(0, viewId.lastIndexOf('.')));
-                            builder.append(contextSuffixes[0]);
-                        }
-                        else
-                        {
-                            builder.append(viewId);
-                            builder.append(contextSuffixes[0]);
-                        }
+                    else if(viewId.lastIndexOf('.') != -1 )
+                    {
+                        builder.append(viewId.substring(0, viewId.lastIndexOf('.')));
+                        builder.append(contextSuffixes[0]);
+                    }
+                    else
+                    {
+                        builder.append(viewId);
+                        builder.append(contextSuffixes[0]);
                     }
-                }
-                else
-                {
-                    builder.append(mapping.getPrefix());
-                    builder.append(viewId);
                 }
             }
-            else
+            else if (mapping.isPrefixMapping())
             {
+                builder.append(mapping.getPrefix());
                 builder.append(viewId);
             }
         }
+        else
+        {
+            builder.append(viewId);
+        }
         
         //JSF 2.2 check view protection.
         if (ViewProtectionUtils.isViewProtected(context, viewId))
@@ -376,52 +349,6 @@ public class DefaultViewHandlerSupport implements ViewHandlerSupport
         return mapping;
     }
 
-    /**
-     * Determines the mapping of the FacesServlet in the web.xml configuration
-     * file. However, there is no need to actually parse this configuration file
-     * as runtime information is sufficient.
-     *
-     * @param servletPath The servletPath of the current request
-     * @param pathInfo    The pathInfo of the current request
-     * @return the mapping of the FacesServlet in the web.xml configuration file
-     */
-    @Deprecated
-    protected static FacesServletMapping calculateFacesServletMapping(
-        String servletPath, String pathInfo)
-    {
-        if (pathInfo != null)
-        {
-            // If there is a "extra path", it's definitely no extension mapping.
-            // Now we just have to determine the path which has been specified
-            // in the url-pattern, but that's easy as it's the same as the
-            // current servletPath. It doesn't even matter if "/*" has been used
-            // as in this case the servletPath is just an empty string according
-            // to the Servlet Specification (SRV 4.4).
-            return FacesServletMapping.createPrefixMapping(servletPath);
-        }
-        else
-        {
-            // In the case of extension mapping, no "extra path" is available.
-            // Still it's possible that prefix-based mapping has been used.
-            // Actually, if there was an exact match no "extra path"
-            // is available (e.g. if the url-pattern is "/faces/*"
-            // and the request-uri is "/context/faces").
-            int slashPos = servletPath.lastIndexOf('/');
-            int extensionPos = servletPath.lastIndexOf('.');
-            if (extensionPos > -1 && extensionPos > slashPos)
-            {
-                String extension = servletPath.substring(extensionPos);
-                return FacesServletMapping.createExtensionMapping(extension);
-            }
-            else
-            {
-                // There is no extension in the given servletPath and therefore
-                // we assume that it's an exact match using prefix-based mapping.
-                return FacesServletMapping.createPrefixMapping(servletPath);
-            }
-        }
-    }
-
     protected String[] getContextSuffix(FacesContext context)
     {
         String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
@@ -627,101 +554,48 @@ public class DefaultViewHandlerSupport implements ViewHandlerSupport
         return null;
     }
     
-    protected boolean checkResourceExists(FacesContext context, String viewId)
+    protected boolean checkResourceExists(FacesContext facesContext, String viewId)
     {
+        if (checkedViewIdsCache == null)
+        {
+            checkedViewIdsCache = CheckedViewIdsCache.getInstance(facesContext);
+        }
+
         try
         {
-            if (isCheckedViewIdCachingEnabled(context))
+            Boolean resourceExists = null;
+            if (checkedViewIdsCache.isEnabled())
             {
-                Boolean resourceExists = getCheckedViewIDMap(context).get(
-                        viewId);
-                if (resourceExists == null)
-                {
-                    ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
-                            .getViewDeclarationLanguage(context, viewId);
-                    if (vdl != null)
-                    {
-                        resourceExists = vdl.viewExists(context, viewId);
-                    }
-                    else
-                    {
-                        // Fallback to default strategy
-                        resourceExists = context.getExternalContext().getResource(
-                                viewId) != null;
-                    }
-                    getCheckedViewIDMap(context).put(viewId, resourceExists);
-                }
-                return resourceExists;
+                resourceExists = checkedViewIdsCache.getCache().get(viewId);
             }
-            else
+
+            if (resourceExists == null)
             {
-                ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
-                            .getViewDeclarationLanguage(context, viewId);
+                ViewDeclarationLanguage vdl = facesContext.getApplication().getViewHandler()
+                        .getViewDeclarationLanguage(facesContext, viewId);
                 if (vdl != null)
                 {
-                    if (vdl.viewExists(context, viewId))
-                    {
-                        return true;
-                    }
+                    resourceExists = vdl.viewExists(facesContext, viewId);
                 }
                 else
                 {
                     // Fallback to default strategy
-                    if (context.getExternalContext().getResource(viewId) != null)
-                    {
-                        return true;
-                    }
+                    resourceExists = facesContext.getExternalContext().getResource(viewId) != null;
+                }
+
+                if (checkedViewIdsCache.isEnabled())
+                {
+                    checkedViewIdsCache.getCache().put(viewId, resourceExists);
                 }
             }
+
+            return resourceExists;
         }
-        catch(MalformedURLException e)
+        catch (MalformedURLException e)
         {
             //ignore and move on
         }     
         return false;
     }
 
-    private ConcurrentLRUCache<String, Boolean> getCheckedViewIDMap(FacesContext context)
-    {
-        if (_checkedViewIdMap == null)
-        {
-            int maxSize = getViewIDCacheMaxSize(context);
-            _checkedViewIdMap = new ConcurrentLRUCache<String, Boolean>((maxSize * 4 + 3) / 3, maxSize);
-        }
-        return _checkedViewIdMap;
-    }
-
-    private boolean isCheckedViewIdCachingEnabled(FacesContext context)
-    {
-        if (_checkedViewIdCacheEnabled == null)
-        {
-            // first, check if the ProjectStage is development and skip caching in this case
-            if (context.isProjectStage(ProjectStage.Development))
-            {
-                _checkedViewIdCacheEnabled = Boolean.FALSE;
-            }
-            else
-            {
-                // in all ohter cases, make sure that the cache is not explicitly disabled via context param
-                _checkedViewIdCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(),
-                        CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
-                        CHECKED_VIEWID_CACHE_ENABLED_DEFAULT);
-            }
-
-            if (log.isLoggable(Level.FINE))
-            {
-                log.log(Level.FINE, "MyFaces ViewID Caching Enabled="
-                        + _checkedViewIdCacheEnabled);
-            }
-        }
-        return _checkedViewIdCacheEnabled;
-    }
-
-    private int getViewIDCacheMaxSize(FacesContext context)
-    {
-        ExternalContext externalContext = context.getExternalContext();
-
-        return WebConfigParamUtils.getIntegerInitParameter(externalContext,
-                CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE, CHECKED_VIEWID_CACHE_DEFAULT_SIZE);
-    }
 }
diff --git a/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java b/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java
index a27c9bd..fe3898d 100644
--- a/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java
+++ b/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMapping.java
@@ -37,7 +37,7 @@ public class FacesServletMapping
      */
     private String extension;
     
-    private boolean exact;
+    private String exact;
 
     /**
      * Creates a new FacesServletMapping object using prefix mapping.
@@ -68,6 +68,13 @@ public class FacesServletMapping
         return mapping;
     }
 
+    public static FacesServletMapping createExactMapping(String exact)
+    {
+        FacesServletMapping mapping = new FacesServletMapping();
+        mapping.setExact(exact);
+        return mapping;
+    }
+    
     /**
      * Returns the path ("/faces", for example) which has been specified in
      * the url-pattern of the FacesServlet mapping. If this mapping is based
@@ -149,7 +156,7 @@ public class FacesServletMapping
     {
         if (isExtensionMapping())
         {
-            return "*" + extension;
+            return '*' + extension;
         }
         else
         {
@@ -157,18 +164,28 @@ public class FacesServletMapping
         }
     }
 
-    public boolean isExact()
+    public String getExact()
     {
         return exact;
     }
 
-    public void setExact(boolean exact)
+    public void setExact(String exact)
     {
         this.exact = exact;
     }
 
     public boolean isExactMapping()
     {
-        return exact;
+        return exact != null;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "FacesServletMapping{" +
+                "prefix='" + prefix + '\'' +
+                ", extension='" + extension + '\'' +
+                ", exact='" + exact + '\'' +
+                '}';
     }
 }
\ No newline at end of file
diff --git a/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMappingUtils.java b/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMappingUtils.java
index 74fa269..6fb71c2 100644
--- a/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMappingUtils.java
+++ b/shared/src/main/java/org/apache/myfaces/shared/application/FacesServletMappingUtils.java
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.myfaces.shared.application;
 
 import java.util.Collection;
@@ -156,7 +155,6 @@ public class FacesServletMappingUtils
                 return calculateFacesServletMapping(servletPath, pathInfo);
             }
         }
-        //return null;
     }
     
     private static FacesServletMapping createMappingFromServletRegistration(FacesContext facesContext, 
@@ -189,8 +187,7 @@ public class FacesServletMappingUtils
                         }
                         else if (allowExactMatch && mapping.startsWith("/") && mapping.equals(servletPath))
                         {
-                            facesExactMapping = FacesServletMapping.createPrefixMapping(servletPath);
-                            facesExactMapping.setExact(true);
+                            facesExactMapping = FacesServletMapping.createExactMapping(servletPath);
                         }
                     }
                 }
@@ -265,9 +262,7 @@ public class FacesServletMappingUtils
             {
                 // There is no extension in the given servletPath and therefore
                 // we assume that it's an exact match using prefix-based mapping.
-                FacesServletMapping mapping = FacesServletMapping.createPrefixMapping(servletPath);
-                mapping.setExact(true);
-                return mapping;
+                return FacesServletMapping.createExactMapping(servletPath);
             }
         }
     }
@@ -284,10 +279,7 @@ public class FacesServletMappingUtils
                 {
                     if (!mapping.contains("*") && prefixedExactMappingViewId.equals(mapping))
                     {
-                        FacesServletMapping facesServletMapping =
-                                FacesServletMapping.createPrefixMapping(prefixedExactMappingViewId);
-                        facesServletMapping.setExact(true);
-                        return facesServletMapping;
+                        return FacesServletMapping.createExactMapping(prefixedExactMappingViewId);
                     }
                 }
             }
@@ -297,7 +289,7 @@ public class FacesServletMappingUtils
     }
     
     
-    public static FacesServletMapping getPrefixOrSuffixMapping(FacesContext facesContext, String viewId)
+    public static FacesServletMapping getGenericPrefixOrSuffixMapping(FacesContext facesContext)
     {
         if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext()))
         {
@@ -310,10 +302,7 @@ public class FacesServletMappingUtils
                     if (isExtensionMapping(mapping))
                     {
                         String extension = extractExtension(mapping);
-                        if (viewId.endsWith(extension))
-                        {
-                            return FacesServletMapping.createExtensionMapping(extension);
-                        }
+                        return FacesServletMapping.createExtensionMapping(extension);
                     }
                     else if (isPrefixMapping(mapping))
                     {