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 2020/04/19 11:26:16 UTC

[myfaces] branch 3.0.x updated: MYFACES-4330

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

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


The following commit(s) were added to refs/heads/3.0.x by this push:
     new f8c308d  MYFACES-4330
f8c308d is described below

commit f8c308d355f446b644e58d9bac829088d35e2816
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Sun Apr 19 13:26:09 2020 +0200

    MYFACES-4330
---
 .../myfaces/application/ResourceHandlerImpl.java   | 3905 ++++++++++----------
 1 file changed, 1954 insertions(+), 1951 deletions(-)

diff --git a/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java b/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java
index 3b64b4e..dfbeda7 100644
--- a/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/application/ResourceHandlerImpl.java
@@ -1,1951 +1,1954 @@
-/*
- * 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.application;
-
-import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
-import org.apache.myfaces.shared.resource.ResourceHandlerCache;
-import org.apache.myfaces.shared.resource.ResourceHandlerCache.ResourceValue;
-import org.apache.myfaces.shared.resource.ResourceHandlerSupport;
-import org.apache.myfaces.shared.resource.ResourceImpl;
-import org.apache.myfaces.shared.resource.ResourceLoader;
-import org.apache.myfaces.shared.resource.ResourceMeta;
-import org.apache.myfaces.shared.resource.ResourceValidationUtils;
-import org.apache.myfaces.shared.util.ClassUtils;
-import org.apache.myfaces.shared.util.ExternalContextUtils;
-import org.apache.myfaces.shared.util.StringUtils;
-import org.apache.myfaces.shared.util.WebConfigParamUtils;
-
-import jakarta.faces.application.Resource;
-import jakarta.faces.application.ResourceHandler;
-import jakarta.faces.application.ResourceWrapper;
-import jakarta.faces.context.ExternalContext;
-import jakarta.faces.context.FacesContext;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.StringTokenizer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-import jakarta.faces.application.ResourceVisitOption;
-import jakarta.faces.application.ViewHandler;
-import jakarta.faces.view.ViewDeclarationLanguage;
-import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
-import org.apache.myfaces.shared.resource.ContractResource;
-import org.apache.myfaces.shared.resource.ContractResourceLoader;
-import org.apache.myfaces.shared.resource.ResourceCachedInfo;
-import org.apache.myfaces.util.SkipMatchIterator;
-
-/**
- * DOCUMENT ME!
- *
- * @author Simon Lessard (latest modification by $Author$)
- * 
- * @version $Revision$ $Date$
- */
-public class ResourceHandlerImpl extends ResourceHandler
-{
-
-    private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
-
-    private ResourceHandlerSupport _resourceHandlerSupport;
-
-    private ResourceHandlerCache _resourceHandlerCache;
-
-    private static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName());
-
-    /**
-     * Allow slash in the library name of a Resource. 
-     */
-    @JSFWebConfigParam(since="2.1.6, 2.0.12", defaultValue="false", 
-            expectedValues="true, false", group="resources")
-    public static final String INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME = 
-            "org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME";
-    public static final boolean INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT = false;
-    
-    /**
-     * Define the default buffer size that is used between Resource.getInputStream() and 
-     * httpServletResponse.getOutputStream() when rendering resources using the default
-     * ResourceHandler.
-     */
-    @JSFWebConfigParam(since="2.1.10, 2.0.16", defaultValue="2048", group="resources")
-    public static final String INIT_PARAM_RESOURCE_BUFFER_SIZE = "org.apache.myfaces.RESOURCE_BUFFER_SIZE";
-    public static final int INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT = 2048;
-    
-    public static final Pattern LIBRARY_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*");
-    public static final Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*\\..*");    
-    
-    private Boolean _allowSlashLibraryName;
-    private int _resourceBufferSize = -1;
-    
-    private String[] _excludedResourceExtensions;
-    
-    private static final String[] FACELETS_VIEW_MAPPINGS_PARAM = {ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME,
-            "facelets.VIEW_MAPPINGS"};
-    private Set<String> _viewSuffixes = null;
-    
-    private final static String MYFACES_JS_RESOURCE_NAME = "oamSubmit.js";
-    public final static String RENDERED_RESOURCES_SET = "org.apache.myfaces.RENDERED_RESOURCES_SET";
-    private final static String MYFACES_LIBRARY_NAME = "org.apache.myfaces";
-
-    private static final String SHARED_STRING_BUILDER = ResourceHandlerImpl.class.getName() + ".SHARED_STRING_BUILDER";
-
-    @Override
-    public Resource createResource(String resourceName)
-    {
-        return createResource(resourceName, null);
-    }
-
-    @Override
-    public Resource createResource(String resourceName, String libraryName)
-    {
-        return createResource(resourceName, libraryName, null);
-    }
-
-    @Override
-    public Resource createResource(String resourceName, String libraryName,
-            String contentType)
-    {
-        Resource resource = null;
-        
-        if (resourceName == null) 
-        {
-            throw new NullPointerException();
-        }
-
-        if (resourceName.length() == 0)
-        {
-            return null;
-        }
-
-        if (resourceName.charAt(0) == '/')
-        {
-            // If resourceName starts with '/', remove that character because it
-            // does not have any meaning (with and without should point to the 
-            // same resource).
-            resourceName = resourceName.substring(1);
-        }        
-        if (!ResourceValidationUtils.isValidResourceName(resourceName))
-        {
-            return null;
-        }
-        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
-                libraryName, isAllowSlashesLibraryName()))
-        {
-            return null;
-        }
-        FacesContext facesContext = FacesContext.getCurrentInstance();
-        if (contentType == null)
-        {
-            //Resolve contentType using ExternalContext.getMimeType
-            contentType = facesContext.getExternalContext().getMimeType(resourceName);
-        }
-
-        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
-        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
-        String contractPreferred = getContractNameForLocateResource(facesContext);
-        ResourceValue resourceValue = null;
-
-        // Check cache:
-        //
-        // Contracts are on top of everything, because it is a concept that defines
-        // resources in a application scope concept. It means all resources in
-        // /resources or /META-INF/resources can be overriden using a contract. Note
-        // it also means resources under /META-INF/flows can also be overriden using
-        // a contract.
-        
-        // Check first the preferred contract if any. If not found, try the remaining
-        // contracts and finally if not found try to found a resource without a 
-        // contract name.
-        if (contractPreferred != null)
-        {
-            resourceValue = getResourceLoaderCache().getResource(
-                    resourceName, libraryName, contentType, localePrefix, contractPreferred);
-        }
-        if (resourceValue == null && !contracts.isEmpty())
-        {
-            // Try to get resource but try with a contract name
-            for (String contract : contracts)
-            {
-                resourceValue = getResourceLoaderCache().getResource(
-                    resourceName, libraryName, contentType, localePrefix, contract);
-                if (resourceValue != null)
-                {
-                    break;
-                }
-            }
-        }
-        // Only if no contract preferred try without it.
-        if (resourceValue == null)
-        {
-            // Try to get resource without contract name
-            resourceValue = getResourceLoaderCache().getResource(resourceName, libraryName, contentType, localePrefix);
-        }
-        
-        if(resourceValue != null)
-        {
-            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
-                    getResourceHandlerSupport(), contentType, 
-                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
-                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
-        }
-        else
-        {
-            boolean resolved = false;
-            // Try preferred contract first
-            if (contractPreferred != null)
-            {
-                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveResourceMeta(loader, resourceName, libraryName, 
-                        localePrefix, contractPreferred);
-                    if (resourceMeta != null)
-                    {
-                        resource = new ResourceImpl(resourceMeta, loader, 
-                            getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
-                                localePrefix, contractPreferred, resourceMeta, loader, 
-                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-                        resolved = true;
-                        break;
-                    }
-                }
-            }
-            if (!resolved && !contracts.isEmpty())
-            {
-                for (ContractResourceLoader loader : 
-                    getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    for (String contract : contracts)
-                    {
-                        ResourceMeta resourceMeta = deriveResourceMeta(
-                            loader, resourceName, libraryName, 
-                            localePrefix, contract);
-                        if (resourceMeta != null)
-                        {
-                            resource = new ResourceImpl(resourceMeta, loader, 
-                                getResourceHandlerSupport(), contentType);
-
-                            // cache it
-                            getResourceLoaderCache().putResource(
-                                    resourceName, libraryName, contentType,
-                                    localePrefix, contract, resourceMeta, loader,
-                                    new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-                            resolved = true;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (!resolved)
-            {
-                for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveResourceMeta(
-                        loader, resourceName, libraryName, localePrefix);
-
-                    if (resourceMeta != null)
-                    {
-                        resource = new ResourceImpl(
-                            resourceMeta, loader, getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
-                                localePrefix, null, resourceMeta, loader, 
-                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-                        break;
-                    }
-                }
-            }
-        }
-        return resource;
-    }
-
-    protected ResourceMeta deriveResourceMeta(ContractResourceLoader resourceLoader,
-            String resourceName, String libraryName, String localePrefix, String contractName)
-    {
-        String resourceVersion = null;
-        String libraryVersion = null;
-        ResourceMeta resourceId = null;
-        
-        //1. Try to locate resource in a localized path
-        if (localePrefix != null)
-        {
-            if (null != libraryName)
-            {
-                String pathToLib = localePrefix + '/' + libraryName;
-                libraryVersion = resourceLoader.getLibraryVersion(pathToLib, contractName);
-
-                if (null != libraryVersion)
-                {
-                    String pathToResource = localePrefix + '/'
-                            + libraryName + '/' + libraryVersion + '/'
-                            + resourceName;
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource, contractName);
-                }
-                else
-                {
-                    String pathToResource = localePrefix + '/'
-                            + libraryName + '/' + resourceName;
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource, contractName);
-                }
-
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {
-                    resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
-                            libraryVersion, resourceName, resourceVersion, contractName);
-                }
-            }
-            else
-            {
-                resourceVersion = resourceLoader
-                        .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
-                            resourceName, resourceVersion, contractName);
-                }
-            }
-
-            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
-            {
-                resourceId = null;
-            }            
-        }
-        
-        //2. Try to localize resource in a non localized path
-        if (resourceId == null)
-        {
-            if (null != libraryName)
-            {
-                libraryVersion = resourceLoader.getLibraryVersion(libraryName, contractName);
-
-                if (null != libraryVersion)
-                {
-                    String pathToResource = (libraryName + '/' + libraryVersion
-                            + '/' + resourceName);
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource, contractName);
-                }
-                else
-                {
-                    String pathToResource = (libraryName + '/'
-                            + resourceName);
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource, contractName);
-                }
-
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(null, libraryName,
-                            libraryVersion, resourceName, resourceVersion, contractName);
-                }
-            }
-            else
-            {
-                resourceVersion = resourceLoader
-                        .getResourceVersion(resourceName, contractName);
-                
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(null, null, null,
-                            resourceName, resourceVersion, contractName);
-                }
-            }
-
-            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
-            {
-                resourceId = null;
-            }            
-        }
-        
-        return resourceId;
-    }
-    
-    /**
-     * This method try to create a ResourceMeta for a specific resource
-     * loader. If no library, or resource is found, just return null,
-     * so the algorithm in createResource can continue checking with the 
-     * next registered ResourceLoader. 
-     */
-    protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
-            String resourceName, String libraryName, String localePrefix)
-    {
-        String resourceVersion = null;
-        String libraryVersion = null;
-        ResourceMeta resourceId = null;
-        
-        //1. Try to locate resource in a localized path
-        if (localePrefix != null)
-        {
-            if (null != libraryName)
-            {
-                String pathToLib = localePrefix + '/' + libraryName;
-                libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
-
-                if (null != libraryVersion)
-                {
-                    String pathToResource = localePrefix + '/'
-                            + libraryName + '/' + libraryVersion + '/'
-                            + resourceName;
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource);
-                }
-                else
-                {
-                    String pathToResource = localePrefix + '/'
-                            + libraryName + '/' + resourceName;
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource);
-                }
-
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {
-                    resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
-                            libraryVersion, resourceName, resourceVersion);
-                }
-            }
-            else
-            {
-                resourceVersion = resourceLoader
-                        .getResourceVersion(localePrefix + '/'+ resourceName);
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
-                            resourceName, resourceVersion);
-                }
-            }
-
-            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
-            {
-                resourceId = null;
-            }            
-        }
-        
-        //2. Try to localize resource in a non localized path
-        if (resourceId == null)
-        {
-            if (null != libraryName)
-            {
-                libraryVersion = resourceLoader.getLibraryVersion(libraryName);
-
-                if (null != libraryVersion)
-                {
-                    String pathToResource = (libraryName + '/' + libraryVersion
-                            + '/' + resourceName);
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource);
-                }
-                else
-                {
-                    String pathToResource = (libraryName + '/'
-                            + resourceName);
-                    resourceVersion = resourceLoader
-                            .getResourceVersion(pathToResource);
-                }
-
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(null, libraryName,
-                            libraryVersion, resourceName, resourceVersion);
-                }
-            }
-            else
-            {
-                resourceVersion = resourceLoader
-                        .getResourceVersion(resourceName);
-                
-                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-                {               
-                    resourceId = resourceLoader.createResourceMeta(null, null, null,
-                            resourceName, resourceVersion);
-                }
-            }
-
-            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
-            {
-                resourceId = null;
-            }            
-        }
-        
-        return resourceId;
-    }
-
-    @Override
-    public String getRendererTypeForResourceName(String resourceName)
-    {
-        if (resourceName.endsWith(".js"))
-        {
-            return "jakarta.faces.resource.Script";
-        }
-        else if (resourceName.endsWith(".css"))
-        {
-            return "jakarta.faces.resource.Stylesheet";
-        }
-        return null;
-    }
-
-    /**
-     *  Handle the resource request, writing in the output. 
-     *  
-     *  This method implements an algorithm semantically identical to 
-     *  the one described on the javadoc of ResourceHandler.handleResourceRequest 
-     */
-    @Override
-    public void handleResourceRequest(FacesContext facesContext) throws IOException
-    {
-        //try
-        //{
-            String resourceBasePath = getResourceHandlerSupport()
-                    .calculateResourceBasePath(facesContext);
-    
-            if (resourceBasePath == null)
-            {
-                // No base name could be calculated, so no further
-                //advance could be done here. HttpServletResponse.SC_NOT_FOUND
-                //cannot be returned since we cannot extract the 
-                //resource base name
-                return;
-            }
-    
-            // We neet to get an instance of HttpServletResponse, but sometimes
-            // the response object is wrapped by several instances of 
-            // ServletResponseWrapper (like ResponseSwitch).
-            // Since we are handling a resource, we can expect to get an 
-            // HttpServletResponse.
-            ExternalContext extContext = facesContext.getExternalContext();
-            Object response = extContext.getResponse();
-            HttpServletResponse httpServletResponse = ExternalContextUtils.getHttpServletResponse(response);
-            if (httpServletResponse == null)
-            {
-                throw new IllegalStateException("Could not obtain an instance of HttpServletResponse.");
-            }
-    
-            if (isResourceIdentifierExcluded(facesContext, resourceBasePath))
-            {
-                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-    
-            String resourceName = null;
-            if (resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
-            {
-                resourceName = resourceBasePath
-                        .substring(ResourceHandler.RESOURCE_IDENTIFIER.length() + 1);
-                
-                if (resourceBasePath != null && !ResourceValidationUtils.isValidResourceName(resourceName))
-                {
-                    httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                    return;
-                }
-            }
-            else
-            {
-                //Does not have the conditions for be a resource call
-                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-    
-            String libraryName = facesContext.getExternalContext()
-                    .getRequestParameterMap().get("ln");
-    
-            if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
-                    libraryName, isAllowSlashesLibraryName()))
-            {
-                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-            
-            Resource resource = null;
-            if (libraryName != null)
-            {
-                //log.info("libraryName=" + libraryName);
-                resource = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName);
-            }
-            else
-            {
-                resource = facesContext.getApplication().getResourceHandler().createResource(resourceName);
-            }
-    
-            if (resource == null)
-            {
-                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-    
-            if (!resource.userAgentNeedsUpdate(facesContext))
-            {
-                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-                return;
-            }
-    
-            httpServletResponse.setContentType(_getContentType(resource, facesContext.getExternalContext()));
-    
-            Map<String, String> headers = resource.getResponseHeaders();
-    
-            for (Map.Entry<String, String> entry : headers.entrySet())
-            {
-                httpServletResponse.setHeader(entry.getKey(), entry.getValue());
-            }
-    
-            // Sets the preferred buffer size for the body of the response
-            extContext.setResponseBufferSize(this.getResourceBufferSize());
-            
-            //serve up the bytes (taken from trinidad ResourceServlet)
-            try
-            {
-                InputStream in = resource.getInputStream();
-                OutputStream out = httpServletResponse.getOutputStream();
-                //byte[] buffer = new byte[_BUFFER_SIZE];
-                byte[] buffer = new byte[this.getResourceBufferSize()];
-    
-                try
-                {
-                    int count = pipeBytes(in, out, buffer);
-                    //set the content lenght
-                    if (!httpServletResponse.isCommitted())
-                    {
-                        httpServletResponse.setContentLength(count);
-                    }
-                }
-                finally
-                {
-                    try
-                    {
-                        in.close();
-                    }
-                    finally
-                    {
-                        out.close();
-                    }
-                }
-            }
-            catch (IOException e)
-            {
-                //TODO: Log using a localized message (which one?)
-                if (isConnectionAbort(e))
-                {
-                    log.log(Level.INFO,"Connection was aborted while loading resource " + resourceName
-                            + " with library " + libraryName);
-                }
-                else
-                {
-                    if (log.isLoggable(Level.WARNING))
-                    {
-                        log.log(Level.WARNING,"Error trying to load and send resource " + resourceName
-                                + " with library " + libraryName + " :"
-                                + e.getMessage(), e);
-                    }
-                    httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                }
-            }
-        //}
-        //catch (Throwable ex)
-        //{
-            // handle the Throwable accordingly. Maybe generate an error page.
-            // FIXME we are creating a html error page for a non html request here
-            // shouln't we do something better? -=Jakob Korherr=-
-            //ErrorPageWriter.handleThrowable(facesContext, ex);
-        //}
-    }
-
-    private static boolean isConnectionAbort(IOException e)
-    {
-        return e.getClass().getCanonicalName().equals("org.apache.catalina.connector.ClientAbortException");
-    }
-
-    /**
-     * Reads the specified input stream into the provided byte array storage and
-     * writes it to the output stream.
-     */
-    private static int pipeBytes(InputStream in, OutputStream out, byte[] buffer)
-            throws IOException
-    {
-        int count = 0;
-        int length;
-
-        while ((length = (in.read(buffer))) >= 0)
-        {
-            out.write(buffer, 0, length);
-            count += length;
-        }
-        return count;
-    }
-
-    @Override
-    public boolean isResourceRequest(FacesContext facesContext)
-    {
-        // Since this method could be called many times we save it
-        // on request map so the first time is calculated it remains
-        // alive until the end of the request
-        Boolean value = (Boolean) facesContext.getAttributes().get(IS_RESOURCE_REQUEST);
-
-        if (value == null)
-        {
-            String resourceBasePath = getResourceHandlerSupport()
-                    .calculateResourceBasePath(facesContext);
-
-            value = resourceBasePath != null
-                    && resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER);
-            facesContext.getAttributes().put(IS_RESOURCE_REQUEST, value);
-        }
-        return value;
-    }
-
-    protected String getLocalePrefixForLocateResource()
-    {
-        return getLocalePrefixForLocateResource(FacesContext.getCurrentInstance());
-    }
-
-    protected String getLocalePrefixForLocateResource(FacesContext context)
-    {
-        String localePrefix = null;
-        boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
-
-        if (isResourceRequest)
-        {
-            localePrefix = context.getExternalContext().getRequestParameterMap().get("loc");
-            
-            if (localePrefix != null)
-            {
-                if (!ResourceValidationUtils.isValidLocalePrefix(localePrefix))
-                {
-                    return null;
-                }
-                return localePrefix;
-            }
-        }
-        
-        String bundleName = context.getApplication().getMessageBundle();
-
-        if (null != bundleName)
-        {
-            Locale locale = null;
-            
-            if (isResourceRequest || context.getViewRoot() == null)
-            {
-                locale = context.getApplication().getViewHandler()
-                                .calculateLocale(context);
-            }
-            else
-            {
-                locale = context.getViewRoot().getLocale();
-            }
-
-            try
-            {
-                ResourceBundle bundle = ResourceBundle
-                        .getBundle(bundleName, locale, ClassUtils.getContextClassLoader());
-
-                if (bundle != null)
-                {
-                    localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
-                }
-            }
-            catch (MissingResourceException e)
-            {
-                // Ignore it and return null
-            }
-        }
-        return localePrefix;
-    }
-    
-    protected String getContractNameForLocateResource(FacesContext context)
-    {
-        String contractName = null;
-        boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
-
-        if (isResourceRequest)
-        {
-            contractName = context.getExternalContext().getRequestParameterMap().get("con");
-        }
-        
-        // Check if the contract has been injected.
-        if (contractName == null)
-        {
-            contractName = (String) context.getAttributes().get(ContractResource.CONTRACT_SELECTED);
-        }
-        
-        //Validate
-        if (contractName != null &&
-            !ResourceValidationUtils.isValidContractName(contractName))
-        {
-            return null;
-        }
-        return contractName;
-    }
-
-    protected boolean isResourceIdentifierExcluded(FacesContext context, String resourceIdentifier)
-    {
-        if (_excludedResourceExtensions == null)
-        {
-            String value = WebConfigParamUtils.getStringInitParameter(context.getExternalContext(),
-                            RESOURCE_EXCLUDES_PARAM_NAME,
-                            RESOURCE_EXCLUDES_DEFAULT_VALUE);
-            
-            _excludedResourceExtensions = StringUtils.splitShortString(value, ' ');
-        }
-        
-        for (int i = 0; i < _excludedResourceExtensions.length; i++)
-        {
-            if (resourceIdentifier.endsWith(_excludedResourceExtensions[i]))
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if a library exists or not. This is done delegating
-     * to each ResourceLoader used, because each one has a different
-     * prefix and way to load resources.
-     * 
-     */
-    @Override
-    public boolean libraryExists(String libraryName)
-    {
-        FacesContext facesContext = FacesContext.getCurrentInstance();
-        String localePrefix = getLocalePrefixForLocateResource(facesContext);
-        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
-
-        String pathToLib = null;
-        Boolean libraryFound = null;
-        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
-                libraryName, isAllowSlashesLibraryName()))
-        {
-            return false;
-        }
-        
-        if (localePrefix != null)
-        {
-            //Check with locale
-            pathToLib = localePrefix + '/' + libraryName;
-
-            libraryFound = getResourceLoaderCache().libraryExists(pathToLib);
-            if (libraryFound != null)
-            {
-                return libraryFound.booleanValue();
-            }
-        }
-        libraryFound = getResourceLoaderCache().libraryExists(libraryName);
-        if (libraryFound != null)
-        {
-            return libraryFound.booleanValue();
-        }
-        
-        if (localePrefix != null)
-        {
-            if (!contracts.isEmpty())
-            {
-                for (String contract : contracts)
-                {
-                    for (ContractResourceLoader loader : getResourceHandlerSupport()
-                            .getContractResourceLoaders())
-                    {
-                        if (loader.libraryExists(pathToLib, contract))
-                        {
-                            getResourceLoaderCache().confirmLibraryExists(pathToLib);
-                            return true;
-                        }
-                    }
-                }
-            }
-            
-            for (ResourceLoader loader : getResourceHandlerSupport()
-                    .getResourceLoaders())
-            {
-                if (loader.libraryExists(pathToLib))
-                {
-                    getResourceLoaderCache().confirmLibraryExists(pathToLib);
-                    return true;
-                }
-            }            
-        }
-
-        //Check without locale
-        if (!contracts.isEmpty())
-        {
-            for (String contract : contracts)
-            {
-                for (ContractResourceLoader loader : getResourceHandlerSupport()
-                        .getContractResourceLoaders())
-                {
-                    if (loader.libraryExists(libraryName, contract))
-                    {
-                        getResourceLoaderCache().confirmLibraryExists(libraryName);
-                        return true;
-                    }
-                }
-            }
-        }
-
-        for (ResourceLoader loader : getResourceHandlerSupport()
-                .getResourceLoaders())
-        {
-            if (loader.libraryExists(libraryName))
-            {
-                getResourceLoaderCache().confirmLibraryExists(libraryName);
-                return true;
-            }
-        }
-
-        if (localePrefix != null)
-        {
-            //Check with locale
-            getResourceLoaderCache().confirmLibraryNotExists(pathToLib);
-        }
-        else
-        {
-            getResourceLoaderCache().confirmLibraryNotExists(libraryName);
-        }
-        return false;
-    }
-
-    /**
-     * @param resourceHandlerSupport
-     *            the resourceHandlerSupport to set
-     */
-    public void setResourceHandlerSupport(
-            ResourceHandlerSupport resourceHandlerSupport)
-    {
-        _resourceHandlerSupport = resourceHandlerSupport;
-    }
-
-    /**
-     * @return the resourceHandlerSupport
-     */
-    protected ResourceHandlerSupport getResourceHandlerSupport()
-    {
-        if (_resourceHandlerSupport == null)
-        {
-            _resourceHandlerSupport = new DefaultResourceHandlerSupport();
-        }
-        return _resourceHandlerSupport;
-    }
-
-    private ResourceHandlerCache getResourceLoaderCache()
-    {
-        if (_resourceHandlerCache == null)
-        {
-            _resourceHandlerCache = new ResourceHandlerCache();
-        }
-        return _resourceHandlerCache;
-    }
-
-    private String _getContentType(Resource resource, ExternalContext externalContext)
-    {
-        String contentType = resource.getContentType();
-
-        // the resource does not provide a content-type --> determine it via mime-type
-        if (contentType == null || contentType.length() == 0)
-        {
-            String resourceName = getWrappedResourceName(resource);
-
-            if (resourceName != null)
-            {
-                contentType = externalContext.getMimeType(resourceName);
-            }
-        }
-
-        return contentType;
-    }
-
-    /**
-     * Recursively unwarp the resource until we find the real resourceName
-     * This is needed because the JSF2 specced ResourceWrapper doesn't override
-     * the getResourceName() method :(
-     * @param resource
-     * @return the first non-null resourceName or <code>null</code> if none set
-     */
-    private String getWrappedResourceName(Resource resource)
-    {
-        String resourceName = resource.getResourceName();
-        if (resourceName != null)
-        {
-            return resourceName;
-        }
-
-        if (resource instanceof ResourceWrapper)
-        {
-            return getWrappedResourceName(((ResourceWrapper) resource).getWrapped());
-        }
-
-        return null;
-    }
-    
-    protected boolean isAllowSlashesLibraryName()
-    {
-        if (_allowSlashLibraryName == null)
-        {
-            _allowSlashLibraryName = WebConfigParamUtils.getBooleanInitParameter(
-                    FacesContext.getCurrentInstance().getExternalContext(), 
-                    INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME,
-                    INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT);
-        }
-        return _allowSlashLibraryName;
-    }
-
-    protected int getResourceBufferSize()
-    {
-        if (_resourceBufferSize == -1)
-        {
-            _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
-                FacesContext.getCurrentInstance().getExternalContext(),
-                INIT_PARAM_RESOURCE_BUFFER_SIZE,
-                INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
-        }
-        return _resourceBufferSize;
-    }
-
-    @Override
-    public Resource createResourceFromId(String resourceId)
-    {
-        Resource resource = null;
-
-        if (resourceId == null)
-        {
-            throw new NullPointerException();
-        }
-        
-        // Later in deriveResourceMeta the resourceId is decomposed and
-        // its elements validated properly.
-        if (!ResourceValidationUtils.isValidResourceId(resourceId))
-        {
-            return null;
-        }
-        
-        FacesContext facesContext = FacesContext.getCurrentInstance();
-        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
-        String contractPreferred = getContractNameForLocateResource(facesContext);
-        ResourceValue resourceValue = null;
-        
-        // Check cache:
-        //
-        // Contracts are on top of everything, because it is a concept that defines
-        // resources in a application scope concept. It means all resources in
-        // /resources or /META-INF/resources can be overriden using a contract. Note
-        // it also means resources under /META-INF/flows can also be overriden using
-        // a contract.
-        if (contractPreferred != null)
-        {
-            resourceValue = getResourceLoaderCache().getResource(
-                    resourceId, contractPreferred);
-        }
-        if (resourceValue == null && !contracts.isEmpty())
-        {
-            // Try to get resource but try with a contract name
-            for (String contract : contracts)
-            {
-                resourceValue = getResourceLoaderCache().getResource(resourceId, contract);
-                if (resourceValue != null)
-                {
-                    break;
-                }
-            }
-        }
-        if (resourceValue == null)
-        {
-            // Try to get resource without contract name
-            resourceValue = getResourceLoaderCache().getResource(resourceId);
-        }
-        
-        if(resourceValue != null)
-        {        
-            //Resolve contentType using ExternalContext.getMimeType
-            String contentType = facesContext.getExternalContext().getMimeType(
-                resourceValue.getResourceMeta().getResourceName());
-
-            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
-                    getResourceHandlerSupport(), contentType,
-                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
-                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
-        }
-        else
-        {
-            boolean resolved = false;
-            if (contractPreferred != null)
-            {
-                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveResourceMeta(
-                        facesContext, loader, resourceId, contractPreferred);
-                    if (resourceMeta != null)
-                    {
-                        String contentType = facesContext.getExternalContext().getMimeType(
-                            resourceMeta.getResourceName());
-                        
-                        resource = new ResourceImpl(resourceMeta, loader, 
-                            getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
-                            new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-                        
-                        resolved = true;
-                        break;
-                    }
-                }
-            }
-            if (!resolved && !contracts.isEmpty())
-            {
-                for (ContractResourceLoader loader : 
-                        getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    for (String contract : contracts)
-                    {
-                        ResourceMeta resourceMeta = deriveResourceMeta(
-                            facesContext, loader, resourceId, contract);
-                        if (resourceMeta != null)
-                        {
-                            String contentType = facesContext.getExternalContext().getMimeType(
-                                resourceMeta.getResourceName());
-
-                            resource = new ResourceImpl(resourceMeta, loader, 
-                                getResourceHandlerSupport(), contentType);
-
-                            // cache it
-                            getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
-                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-
-                            resolved = true;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (!resolved)
-            {
-                for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveResourceMeta(facesContext, loader, resourceId);
-
-                    if (resourceMeta != null)
-                    {
-                        String contentType = facesContext.getExternalContext().getMimeType(
-                            resourceMeta.getResourceName());
-
-                        resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
-                            new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
-                        break;
-                    }
-                }
-            }
-        }
-        return resource;
-    }
-
-    protected ResourceMeta deriveResourceMeta(FacesContext context, ResourceLoader resourceLoader,
-            String resourceId)
-    {
-        ResourceMeta resourceMeta = null;
-        String token = null;
-        String localePrefix = null;
-        String libraryName = null;
-        String libraryVersion = null;
-        String resourceName = null;
-        String resourceVersion = null;
-        
-        // Check if resource exists. It avoids additional 
-        // checks and it can be done very quickly because the 
-        // loader always uses the resourceId structure to
-        // organize resources. But decompose the resourceId is
-        // even faster.
-        //if (resourceLoader.resourceIdExists(resourceId))
-        //{
-        int lastSlash = resourceId.lastIndexOf('/');
-        if (lastSlash < 0)
-        {
-            //no slashes, so it is just a plain resource.
-            resourceName = resourceId;
-        }
-        else
-        {
-            token = resourceId.substring(lastSlash+1);
-            if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
-            {
-                int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
-                if (secondLastSlash < 0)
-                {
-                    secondLastSlash = 0;
-                }
-
-                String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
-                int lastPoint = rnToken.lastIndexOf('.');
-                // lastPoint < 0 means it does not match, the token is not a resource version
-                if (lastPoint >= 0)
-                {
-                    String ext = rnToken.substring(lastPoint);
-                    if (token.endsWith(ext))
-                    {
-                        //It match a versioned resource
-                        resourceVersion = token.substring(0,token.length()-ext.length());
-                    }
-                }
-            }
-
-            // 1. Extract the library path and locale prefix if necessary
-            int start = 0;
-            int firstSlash = resourceId.indexOf('/');
-
-            // At least one slash, check if the start is locale prefix.
-            String bundleName = context.getApplication().getMessageBundle();
-            //If no bundle set, it can't be localePrefix
-            if (null != bundleName)
-            {
-                token = resourceId.substring(start, firstSlash);
-                //Try to derive a locale object
-                Locale locale = _LocaleUtils.deriveLocale(token);
-
-                // If the locale was derived and it is available, 
-                // assume that portion of the resourceId it as a locale prefix.
-                if (locale != null && _LocaleUtils.isAvailableLocale(locale))
-                {
-                    localePrefix = token;
-                    start = firstSlash+1;
-                }
-            }
-
-            //Check slash again from start
-            firstSlash = resourceId.indexOf('/', start);
-            if (firstSlash < 0)
-            {
-                //no slashes.
-                resourceName = resourceId.substring(start);
-            }
-            else
-            {
-                //check libraryName
-                token = resourceId.substring(start, firstSlash);
-                int minResourceNameSlash = (resourceVersion != null) ?
-                    resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
-                //if (resourceLoader.libraryExists(token))
-                if (start < minResourceNameSlash)
-                {
-                    libraryName = token;
-                    start = firstSlash+1;
-
-                    //Now that libraryName exists, check libraryVersion
-                    firstSlash = resourceId.indexOf('/', start);
-                    if (firstSlash >= 0)
-                    {
-                        token = resourceId.substring(start, firstSlash);
-                        if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
-                        {
-                            libraryVersion = token;
-                            start = firstSlash+1;
-                        }
-                    }
-                }
-
-                firstSlash = resourceId.indexOf('/', start);
-                if (firstSlash < 0)
-                {
-                    //no slashes.
-                    resourceName = resourceId.substring(start);
-                }
-                else
-                {
-                    // Check resource version. 
-                    if (resourceVersion != null)
-                    {
-                        resourceName = resourceId.substring(start,lastSlash);
-                    }
-                    else
-                    {
-                        //no resource version, assume the remaining to be resource name
-                        resourceName = resourceId.substring(start);
-                    }
-                }
-            }
-        }
-
-        //Check libraryName and resourceName
-        if (resourceName == null)
-        {
-            return null;
-        }
-        if (!ResourceValidationUtils.isValidResourceName(resourceName))
-        {
-            return null;
-        }
-
-        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
-                libraryName, isAllowSlashesLibraryName()))
-        {
-            return null;
-        }
-
-        // If some variable is "" set it as null.
-        if (localePrefix != null && localePrefix.length() == 0)
-        {
-            localePrefix = null;
-        }
-        if (libraryName != null && libraryName.length() == 0)
-        {
-            libraryName = null;
-        }
-        if (libraryVersion != null && libraryVersion.length() == 0)
-        {
-            libraryVersion = null;
-        }
-        if (resourceName != null && resourceName.length() == 0)
-        {
-            resourceName = null;
-        }
-        if (resourceVersion != null && resourceVersion.length() == 0)
-        {
-            resourceVersion = null;
-        }
-
-        resourceMeta = resourceLoader.createResourceMeta(
-            localePrefix, libraryName, libraryVersion, resourceName, resourceVersion);
-
-        if (resourceMeta != null &&
-            !resourceLoader.resourceExists(resourceMeta))
-        {
-            resourceMeta = null;
-        }
-        //}
-        return resourceMeta;
-    }
-    
-    protected ResourceMeta deriveResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
-            String resourceId, String contractName)
-    {
-        ResourceMeta resourceMeta = null;
-        String token = null;
-        String localePrefix = null;
-        String libraryName = null;
-        String libraryVersion = null;
-        String resourceName = null;
-        String resourceVersion = null;
-        
-        // Check if resource exists. It avoids additional 
-        // checks and it can be done very quickly because the 
-        // loader always uses the resourceId structure to
-        // organize resources. But decompose the resourceId is
-        // even faster.
-        //if (resourceLoader.resourceIdExists(resourceId))
-        //{
-        int lastSlash = resourceId.lastIndexOf('/');
-        if (lastSlash < 0)
-        {
-            //no slashes, so it is just a plain resource.
-            resourceName = resourceId;
-        }
-        else
-        {
-            token = resourceId.substring(lastSlash+1);
-            if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
-            {
-                int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
-                if (secondLastSlash < 0)
-                {
-                    secondLastSlash = 0;
-                }
-
-                String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
-                int lastPoint = rnToken.lastIndexOf('.');
-                // lastPoint < 0 means it does not match, the token is not a resource version
-                if (lastPoint >= 0)
-                {
-                    String ext = rnToken.substring(lastPoint);
-                    if (token.endsWith(ext))
-                    {
-                        //It match a versioned resource
-                        resourceVersion = token.substring(0,token.length()-ext.length());
-                    }
-                }
-            }
-
-            // 1. Extract the library path and locale prefix if necessary
-            int start = 0;
-            int firstSlash = resourceId.indexOf('/');
-
-            // At least one slash, check if the start is locale prefix.
-            String bundleName = context.getApplication().getMessageBundle();
-            //If no bundle set, it can't be localePrefix
-            if (null != bundleName)
-            {
-                token = resourceId.substring(start, firstSlash);
-                //Try to derive a locale object
-                Locale locale = _LocaleUtils.deriveLocale(token);
-
-                // If the locale was derived and it is available, 
-                // assume that portion of the resourceId it as a locale prefix.
-                if (locale != null && _LocaleUtils.isAvailableLocale(locale))
-                {
-                    localePrefix = token;
-                    start = firstSlash+1;
-                }
-            }
-
-            //Check slash again from start
-            firstSlash = resourceId.indexOf('/', start);
-            if (firstSlash < 0)
-            {
-                //no slashes.
-                resourceName = resourceId.substring(start);
-            }
-            else
-            {
-                //check libraryName
-                token = resourceId.substring(start, firstSlash);
-                int minResourceNameSlash = (resourceVersion != null) ?
-                    resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
-                //if (resourceLoader.libraryExists(token))
-                if (start < minResourceNameSlash)
-                {
-                    libraryName = token;
-                    start = firstSlash+1;
-
-                    //Now that libraryName exists, check libraryVersion
-                    firstSlash = resourceId.indexOf('/', start);
-                    if (firstSlash >= 0)
-                    {
-                        token = resourceId.substring(start, firstSlash);
-                        if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
-                        {
-                            libraryVersion = token;
-                            start = firstSlash+1;
-                        }
-                    }
-                }
-
-                firstSlash = resourceId.indexOf('/', start);
-                if (firstSlash < 0)
-                {
-                    //no slashes.
-                    resourceName = resourceId.substring(start);
-                }
-                else
-                {
-                    // Check resource version. 
-                    if (resourceVersion != null)
-                    {
-                        resourceName = resourceId.substring(start,lastSlash);
-                    }
-                    else
-                    {
-                        //no resource version, assume the remaining to be resource name
-                        resourceName = resourceId.substring(start);
-                    }
-                }
-            }
-        }
-
-        //Check libraryName and resourceName
-        if (resourceName == null)
-        {
-            return null;
-        }
-        if (!ResourceValidationUtils.isValidResourceName(resourceName))
-        {
-            return null;
-        }
-
-        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
-                libraryName, isAllowSlashesLibraryName()))
-        {
-            return null;
-        }
-
-        // If some variable is "" set it as null.
-        if (localePrefix != null && localePrefix.length() == 0)
-        {
-            localePrefix = null;
-        }
-        if (libraryName != null && libraryName.length() == 0)
-        {
-            libraryName = null;
-        }
-        if (libraryVersion != null && libraryVersion.length() == 0)
-        {
-            libraryVersion = null;
-        }
-        if (resourceName != null && resourceName.length() == 0)
-        {
-            resourceName = null;
-        }
-        if (resourceVersion != null && resourceVersion.length() == 0)
-        {
-            resourceVersion = null;
-        }
-
-        resourceMeta = resourceLoader.createResourceMeta(
-            localePrefix, libraryName, libraryVersion, resourceName, resourceVersion, contractName);
-
-        if (resourceMeta != null &&
-            !resourceLoader.resourceExists(resourceMeta))
-        {
-            resourceMeta = null;
-        }
-        //}
-        return resourceMeta;
-    }
-    
-    protected ResourceMeta deriveViewResourceMeta(FacesContext context, ResourceLoader resourceLoader,
-            String resourceName, String localePrefix)
-    {
-        ResourceMeta resourceMeta = null;
-        String resourceVersion = null;
-
-        //1. Try to locate resource in a localized path
-        if (localePrefix != null)
-        {
-            resourceVersion = resourceLoader
-                    .getResourceVersion(localePrefix + '/'+ resourceName);
-            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-            {
-                resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
-                         resourceName, resourceVersion);
-            }
-
-            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
-            {
-                resourceMeta = null;
-            }            
-        }
-        
-        //2. Try to localize resource in a non localized path
-        if (resourceMeta == null)
-        {
-            resourceVersion = resourceLoader
-                    .getResourceVersion(resourceName);
-            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-            {
-                resourceMeta = resourceLoader.createResourceMeta(null, null, null,
-                         resourceName, resourceVersion);
-            }
-
-            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
-            {
-                resourceMeta = null;
-            }            
-        }
-
-        return resourceMeta;        
-    }
-    
-    protected ResourceMeta deriveViewResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
-            String resourceName, String localePrefix, String contractName)
-    {
-        ResourceMeta resourceMeta = null;
-        String resourceVersion = null;
-
-        //1. Try to locate resource in a localized path
-        if (localePrefix != null)
-        {
-            resourceVersion = resourceLoader
-                    .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
-            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-            {
-                resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
-                     resourceName, resourceVersion, contractName);
-            }
-
-            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
-            {
-                resourceMeta = null;
-            }            
-        }
-        
-        //2. Try to localize resource in a non localized path
-        if (resourceMeta == null)
-        {
-            resourceVersion = resourceLoader
-                    .getResourceVersion(resourceName, contractName);
-            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
-            {
-                resourceMeta = resourceLoader.createResourceMeta(null, null, null,
-                         resourceName, resourceVersion, contractName);
-            }
-
-            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
-            {
-                resourceMeta = null;
-            }            
-        }
-
-        return resourceMeta;
-    }
-
-    @Override
-    public Resource createViewResource(FacesContext facesContext, String resourceName)
-    {
-        // There are some special points to remember for a view resource in comparison
-        // with a normal resource:
-        //
-        // - A view resource never has an associated library name 
-        //   (this was done to keep simplicity).
-        // - A view resource can be inside a resource library contract.
-        // - A view resource could be internationalized in the same way a normal resource.
-        // - A view resource can be created from the webapp root folder, 
-        //   a normal resource cannot.
-        // - A view resource cannot be created from /resources or META-INF/resources.
-        // 
-        // For example, a valid resourceId for a view resource is like this:
-        //
-        // [localePrefix/]resourceName[/resourceVersion]
-        //
-        // but the resource loader can ignore localePrefix or resourceVersion, like
-        // for example the webapp root folder.
-        // 
-        // When createViewResource() is called, the view must be used to derive
-        // the localePrefix and facesContext must be used to get the available contracts.
-        
-        Resource resource = null;
-
-        if (resourceName == null)
-        {
-            throw new NullPointerException();
-        }
-        if (resourceName.charAt(0) == '/')
-        {
-            // If resourceName starts with '/', remove that character because it
-            // does not have any meaning (with and without should point to the 
-            // same resource).
-            resourceName = resourceName.substring(1);
-        }
-        
-        // Later in deriveResourceMeta the resourceId is decomposed and
-        // its elements validated properly.
-        if (!ResourceValidationUtils.isValidViewResource(resourceName))
-        {
-            return null;
-        }
-        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
-        String contentType = facesContext.getExternalContext().getMimeType(resourceName);
-        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
-        String contractPreferred = getContractNameForLocateResource(facesContext);
-        ResourceValue resourceValue = null;
-        
-        // Check cache:
-        //
-        // Contracts are on top of everything, because it is a concept that defines
-        // resources in a application scope concept. It means all resources in
-        // /resources or /META-INF/resources can be overriden using a contract. Note
-        // it also means resources under /META-INF/flows can also be overriden using
-        // a contract.
-        if (contractPreferred != null)
-        {
-            resourceValue = getResourceLoaderCache().getViewResource(
-                    resourceName, contentType, localePrefix, contractPreferred);
-        }
-        if (resourceValue == null && !contracts.isEmpty())
-        {
-            // Try to get resource but try with a contract name
-            for (String contract : contracts)
-            {
-                resourceValue = getResourceLoaderCache().getViewResource(
-                    resourceName, contentType, localePrefix, contract);
-                if (resourceValue != null)
-                {
-                    break;
-                }
-            }
-        }
-        if (resourceValue == null)
-        {
-            // Try to get resource without contract name
-            resourceValue = getResourceLoaderCache().getViewResource(
-                resourceName, contentType, localePrefix);
-        }
-
-        if(resourceValue != null)
-        {        
-            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
-                    getResourceHandlerSupport(), contentType, 
-                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, null);
-        }
-        else
-        {
-            boolean resolved = false;
-            if (contractPreferred != null)
-            {
-                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveViewResourceMeta(
-                        facesContext, loader, resourceName, localePrefix, contractPreferred);
-                    if (resourceMeta != null)
-                    {
-                        resource = new ResourceImpl(resourceMeta, loader, 
-                            getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putViewResource(
-                            resourceName, contentType, localePrefix, contractPreferred, resourceMeta, loader, 
-                            new ResourceCachedInfo(resource.getURL(), null));
-                        
-                        resolved = true;
-                        break;
-                    }
-                }
-            }
-            if (!resolved && !contracts.isEmpty())
-            {
-                for (ContractResourceLoader loader : 
-                        getResourceHandlerSupport().getContractResourceLoaders())
-                {
-                    for (String contract : contracts)
-                    {
-                        ResourceMeta resourceMeta = deriveViewResourceMeta(
-                            facesContext, loader, resourceName, localePrefix, contract);
-                        if (resourceMeta != null)
-                        {
-                            resource = new ResourceImpl(resourceMeta, loader, 
-                                getResourceHandlerSupport(), contentType);
-
-                            // cache it
-                            getResourceLoaderCache().putViewResource(
-                                resourceName, contentType, localePrefix, contract, resourceMeta, loader,
-                                new ResourceCachedInfo(resource.getURL(), null));
-
-                            resolved = true;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (!resolved)
-            {
-                // "... Considering the web app root ..."
-                
-                // "... Considering faces flows (at the locations specified in the spec prose document section 
-                // Faces Flows in the Using JSF in Web Applications chapter) ..."
-                for (ResourceLoader loader : getResourceHandlerSupport().getViewResourceLoaders())
-                {
-                    ResourceMeta resourceMeta = deriveViewResourceMeta(
-                        facesContext, loader, resourceName, localePrefix);
-
-                    if (resourceMeta != null)
-                    {
-                        resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
-
-                        // cache it
-                        getResourceLoaderCache().putViewResource(
-                            resourceName, contentType, localePrefix, resourceMeta, loader,
-                            new ResourceCachedInfo(resource.getURL(), null));
-                        break;
-                    }
-                }
-            }
-        }
-        return resource;
-    }
-
-    public Stream<java.lang.String> getViewResources(FacesContext facesContext, 
-            String path, int maxDepth, ResourceVisitOption... options)   
-    {
-        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
-        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
-        String contractPreferred = getContractNameForLocateResource(facesContext);
-
-        if (this._viewSuffixes == null)
-        {
-            this._viewSuffixes = loadSuffixes(facesContext.getExternalContext());
-        }
-
-        Iterator it = new FilterInvalidSuffixViewResourceIterator(new ViewResourceIterator(facesContext, 
-                    getResourceHandlerSupport(), localePrefix, contracts,
-                    contractPreferred, path, maxDepth, options), facesContext, _viewSuffixes);
- 
-        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,Spliterator.DISTINCT), false);
-    }
-    
-    private Set<String> loadSuffixes (ExternalContext context)
-    {
-        Set<String> result = new HashSet<String>();
-        String definedSuffixes = WebConfigParamUtils.getStringInitParameter(context, 
-                ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX);
-        StringTokenizer tokenizer;
-        
-        if (definedSuffixes == null) 
-        {
-            definedSuffixes = ViewHandler.DEFAULT_SUFFIX;
-        }
-        
-        // This is a space-separated list of suffixes, so parse them out.
-        
-        tokenizer = new StringTokenizer (definedSuffixes, " ");
-        
-        while (tokenizer.hasMoreTokens()) 
-        {
-            result.add (tokenizer.nextToken());
-        }
-        
-        String faceletSuffix = WebConfigParamUtils.getStringInitParameter(context, 
-                ViewHandler.FACELETS_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_FACELETS_SUFFIX);
-        
-        if (faceletSuffix != null)
-        {
-            result.add(faceletSuffix.trim());
-        }
-        
-        String faceletViewMappings = WebConfigParamUtils.getStringInitParameter(context, FACELETS_VIEW_MAPPINGS_PARAM);
-        
-        if (faceletViewMappings != null)
-        {
-            tokenizer = new StringTokenizer(faceletViewMappings, ";");
-            while (tokenizer.hasMoreTokens()) 
-            {
-                result.add (tokenizer.nextToken());
-            }
-        }
-        
-        return result;
-    }    
-    
-    /*
-     * Filter out views without a valid suffix.
-     */
-    private static class FilterInvalidSuffixViewResourceIterator extends SkipMatchIterator<String>
-    {
-        private FacesContext facesContext;
-        private Set<String> validSuffixes;
-
-        public FilterInvalidSuffixViewResourceIterator(Iterator<String> delegate, FacesContext facesContext,
-                Set<String> validSuffixes)
-        {
-            super(delegate);
-            this.facesContext = facesContext;
-            this.validSuffixes = validSuffixes;
-        }
-
-        @Override
-        protected boolean match(String value)
-        {
-            String viewId = (String) value;
-            ViewDeclarationLanguage vdl = facesContext.getApplication().getViewHandler()
-                    .getViewDeclarationLanguage(facesContext, viewId);
-            if (vdl != null && vdl.viewExists(facesContext, viewId))
-            {
-                boolean matchSuffix = false;
-                for (String suffix : validSuffixes)
-                {
-                    if (suffix != null && suffix.length() > 0 && viewId.endsWith(suffix))
-                    {
-                        matchSuffix = true;
-                        break;
-                    }
-                }
-                if (matchSuffix)
-                {
-                    //There is view, do not match
-                    return false;
-                }
-                else
-                {
-                    return true;
-                }
-            }
-            // It is another resource file, skip
-            return true;
-        }
-    }
-
-    /**
-     * @since 2.3
-     * @param facesContext
-     * @param resourceName
-     * @param libraryName
-     * @return 
-     */
-    public boolean isResourceRendered(FacesContext facesContext, String resourceName, String libraryName)
-    {
-        return getRenderedResources(facesContext).containsKey(
-                libraryName != null
-                        ? contactLibraryAndResource(facesContext, libraryName, resourceName)
-                        : resourceName);
-    }
-
-    /**
-     * @since 2.3
-     * @param facesContext
-     * @param resourceName
-     * @param libraryName 
-     */
-    @Override
-    public void markResourceRendered(FacesContext facesContext, String resourceName, String libraryName)
-    {
-        getRenderedResources(facesContext).put(
-                libraryName != null
-                        ? contactLibraryAndResource(facesContext, libraryName, resourceName)
-                        : resourceName,
-                Boolean.TRUE);
-
-        if (ResourceHandler.JSF_SCRIPT_LIBRARY_NAME.equals(libraryName) &&
-            ResourceHandler.JSF_SCRIPT_RESOURCE_NAME.equals(resourceName))
-        {
-            // If we are calling this method, it is expected myfaces core is being used as runtime and note
-            // oamSubmit script is included inside jsf.js, so mark this one too.
-            getRenderedResources(facesContext).put(
-                    contactLibraryAndResource(facesContext, MYFACES_LIBRARY_NAME, MYFACES_JS_RESOURCE_NAME),
-                    Boolean.TRUE);
-        }
-    }
-    
-    /**
-     * Return a set of already rendered resources by this renderer on the current
-     * request. 
-     * 
-     * @param facesContext
-     * @return
-     */
-    @SuppressWarnings("unchecked")
-    private static Map<String, Boolean> getRenderedResources(FacesContext facesContext)
-    {
-        Map<String, Boolean> map = (Map<String, Boolean>) facesContext.getViewRoot().getTransientStateHelper()
-                .getTransient(RENDERED_RESOURCES_SET);
-        if (map == null)
-        {
-            map = new HashMap<String, Boolean>();
-            facesContext.getViewRoot().getTransientStateHelper().putTransient(RENDERED_RESOURCES_SET,map);
-        }
-        return map;
-    }
-
-    private static String contactLibraryAndResource(FacesContext facesContext, String libraryName, String resourceName)
-    {       
-        StringBuilder sb = SharedStringBuilder.get(facesContext, SHARED_STRING_BUILDER, 40);
-        return sb.append(libraryName).append('/').append(resourceName).toString();
-    }
-}
+/*
+ * 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.application;
+
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
+import org.apache.myfaces.shared.resource.ResourceHandlerCache;
+import org.apache.myfaces.shared.resource.ResourceHandlerCache.ResourceValue;
+import org.apache.myfaces.shared.resource.ResourceHandlerSupport;
+import org.apache.myfaces.shared.resource.ResourceImpl;
+import org.apache.myfaces.shared.resource.ResourceLoader;
+import org.apache.myfaces.shared.resource.ResourceMeta;
+import org.apache.myfaces.shared.resource.ResourceValidationUtils;
+import org.apache.myfaces.shared.util.ClassUtils;
+import org.apache.myfaces.shared.util.ExternalContextUtils;
+import org.apache.myfaces.shared.util.StringUtils;
+import org.apache.myfaces.shared.util.WebConfigParamUtils;
+
+import jakarta.faces.application.Resource;
+import jakarta.faces.application.ResourceHandler;
+import jakarta.faces.application.ResourceWrapper;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import jakarta.faces.application.ResourceVisitOption;
+import jakarta.faces.application.ViewHandler;
+import jakarta.faces.view.ViewDeclarationLanguage;
+import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
+import org.apache.myfaces.shared.resource.ContractResource;
+import org.apache.myfaces.shared.resource.ContractResourceLoader;
+import org.apache.myfaces.shared.resource.ResourceCachedInfo;
+import org.apache.myfaces.util.SkipMatchIterator;
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Simon Lessard (latest modification by $Author$)
+ * 
+ * @version $Revision$ $Date$
+ */
+public class ResourceHandlerImpl extends ResourceHandler
+{
+
+    private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
+
+    private ResourceHandlerSupport _resourceHandlerSupport;
+
+    private ResourceHandlerCache _resourceHandlerCache;
+
+    private static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName());
+
+    /**
+     * Allow slash in the library name of a Resource. 
+     */
+    @JSFWebConfigParam(since="2.1.6, 2.0.12", defaultValue="false", 
+            expectedValues="true, false", group="resources")
+    public static final String INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME = 
+            "org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME";
+    public static final boolean INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT = false;
+    
+    /**
+     * Define the default buffer size that is used between Resource.getInputStream() and 
+     * httpServletResponse.getOutputStream() when rendering resources using the default
+     * ResourceHandler.
+     */
+    @JSFWebConfigParam(since="2.1.10, 2.0.16", defaultValue="2048", group="resources")
+    public static final String INIT_PARAM_RESOURCE_BUFFER_SIZE = "org.apache.myfaces.RESOURCE_BUFFER_SIZE";
+    public static final int INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT = 2048;
+    
+    public static final Pattern LIBRARY_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*");
+    public static final Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*\\..*");    
+    
+    private Boolean _allowSlashLibraryName;
+    private int _resourceBufferSize = -1;
+    
+    private String[] _excludedResourceExtensions;
+    
+    private static final String[] FACELETS_VIEW_MAPPINGS_PARAM = {ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME,
+            "facelets.VIEW_MAPPINGS"};
+    private Set<String> _viewSuffixes = null;
+    
+    private final static String MYFACES_JS_RESOURCE_NAME = "oamSubmit.js";
+    public final static String RENDERED_RESOURCES_SET = "org.apache.myfaces.RENDERED_RESOURCES_SET";
+    private final static String MYFACES_LIBRARY_NAME = "org.apache.myfaces";
+
+    private static final String SHARED_STRING_BUILDER = ResourceHandlerImpl.class.getName() + ".SHARED_STRING_BUILDER";
+
+    @Override
+    public Resource createResource(String resourceName)
+    {
+        return createResource(resourceName, null);
+    }
+
+    @Override
+    public Resource createResource(String resourceName, String libraryName)
+    {
+        return createResource(resourceName, libraryName, null);
+    }
+
+    @Override
+    public Resource createResource(String resourceName, String libraryName,
+            String contentType)
+    {
+        Resource resource = null;
+        
+        if (resourceName == null) 
+        {
+            throw new NullPointerException();
+        }
+
+        if (resourceName.length() == 0)
+        {
+            return null;
+        }
+
+        if (resourceName.charAt(0) == '/')
+        {
+            // If resourceName starts with '/', remove that character because it
+            // does not have any meaning (with and without should point to the 
+            // same resource).
+            resourceName = resourceName.substring(1);
+        }        
+        if (!ResourceValidationUtils.isValidResourceName(resourceName))
+        {
+            return null;
+        }
+        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
+                libraryName, isAllowSlashesLibraryName()))
+        {
+            return null;
+        }
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        if (contentType == null)
+        {
+            //Resolve contentType using ExternalContext.getMimeType
+            contentType = facesContext.getExternalContext().getMimeType(resourceName);
+        }
+
+        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
+        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
+        String contractPreferred = getContractNameForLocateResource(facesContext);
+        ResourceValue resourceValue = null;
+
+        // Check cache:
+        //
+        // Contracts are on top of everything, because it is a concept that defines
+        // resources in a application scope concept. It means all resources in
+        // /resources or /META-INF/resources can be overriden using a contract. Note
+        // it also means resources under /META-INF/flows can also be overriden using
+        // a contract.
+        
+        // Check first the preferred contract if any. If not found, try the remaining
+        // contracts and finally if not found try to found a resource without a 
+        // contract name.
+        if (contractPreferred != null)
+        {
+            resourceValue = getResourceLoaderCache().getResource(
+                    resourceName, libraryName, contentType, localePrefix, contractPreferred);
+        }
+        if (resourceValue == null && !contracts.isEmpty())
+        {
+            // Try to get resource but try with a contract name
+            for (String contract : contracts)
+            {
+                resourceValue = getResourceLoaderCache().getResource(
+                    resourceName, libraryName, contentType, localePrefix, contract);
+                if (resourceValue != null)
+                {
+                    break;
+                }
+            }
+        }
+        // Only if no contract preferred try without it.
+        if (resourceValue == null)
+        {
+            // Try to get resource without contract name
+            resourceValue = getResourceLoaderCache().getResource(resourceName, libraryName, contentType, localePrefix);
+        }
+        
+        if(resourceValue != null)
+        {
+            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
+                    getResourceHandlerSupport(), contentType, 
+                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
+                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
+        }
+        else
+        {
+            boolean resolved = false;
+            // Try preferred contract first
+            if (contractPreferred != null)
+            {
+                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveResourceMeta(loader, resourceName, libraryName, 
+                        localePrefix, contractPreferred);
+                    if (resourceMeta != null)
+                    {
+                        resource = new ResourceImpl(resourceMeta, loader, 
+                            getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
+                                localePrefix, contractPreferred, resourceMeta, loader, 
+                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+                        resolved = true;
+                        break;
+                    }
+                }
+            }
+            if (!resolved && !contracts.isEmpty())
+            {
+                for (ContractResourceLoader loader : 
+                    getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    for (String contract : contracts)
+                    {
+                        ResourceMeta resourceMeta = deriveResourceMeta(
+                            loader, resourceName, libraryName, 
+                            localePrefix, contract);
+                        if (resourceMeta != null)
+                        {
+                            resource = new ResourceImpl(resourceMeta, loader, 
+                                getResourceHandlerSupport(), contentType);
+
+                            // cache it
+                            getResourceLoaderCache().putResource(
+                                    resourceName, libraryName, contentType,
+                                    localePrefix, contract, resourceMeta, loader,
+                                    new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+                            resolved = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!resolved)
+            {
+                for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveResourceMeta(
+                        loader, resourceName, libraryName, localePrefix);
+
+                    if (resourceMeta != null)
+                    {
+                        resource = new ResourceImpl(
+                            resourceMeta, loader, getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
+                                localePrefix, null, resourceMeta, loader, 
+                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+                        break;
+                    }
+                }
+            }
+        }
+        return resource;
+    }
+
+    protected ResourceMeta deriveResourceMeta(ContractResourceLoader resourceLoader,
+            String resourceName, String libraryName, String localePrefix, String contractName)
+    {
+        String resourceVersion = null;
+        String libraryVersion = null;
+        ResourceMeta resourceId = null;
+        
+        //1. Try to locate resource in a localized path
+        if (localePrefix != null)
+        {
+            if (null != libraryName)
+            {
+                String pathToLib = localePrefix + '/' + libraryName;
+                libraryVersion = resourceLoader.getLibraryVersion(pathToLib, contractName);
+
+                if (null != libraryVersion)
+                {
+                    String pathToResource = localePrefix + '/'
+                            + libraryName + '/' + libraryVersion + '/'
+                            + resourceName;
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource, contractName);
+                }
+                else
+                {
+                    String pathToResource = localePrefix + '/'
+                            + libraryName + '/' + resourceName;
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource, contractName);
+                }
+
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {
+                    resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
+                            libraryVersion, resourceName, resourceVersion, contractName);
+                }
+            }
+            else
+            {
+                resourceVersion = resourceLoader
+                        .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
+                            resourceName, resourceVersion, contractName);
+                }
+            }
+
+            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
+            {
+                resourceId = null;
+            }            
+        }
+        
+        //2. Try to localize resource in a non localized path
+        if (resourceId == null)
+        {
+            if (null != libraryName)
+            {
+                libraryVersion = resourceLoader.getLibraryVersion(libraryName, contractName);
+
+                if (null != libraryVersion)
+                {
+                    String pathToResource = (libraryName + '/' + libraryVersion
+                            + '/' + resourceName);
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource, contractName);
+                }
+                else
+                {
+                    String pathToResource = (libraryName + '/'
+                            + resourceName);
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource, contractName);
+                }
+
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(null, libraryName,
+                            libraryVersion, resourceName, resourceVersion, contractName);
+                }
+            }
+            else
+            {
+                resourceVersion = resourceLoader
+                        .getResourceVersion(resourceName, contractName);
+                
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(null, null, null,
+                            resourceName, resourceVersion, contractName);
+                }
+            }
+
+            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
+            {
+                resourceId = null;
+            }            
+        }
+        
+        return resourceId;
+    }
+    
+    /**
+     * This method try to create a ResourceMeta for a specific resource
+     * loader. If no library, or resource is found, just return null,
+     * so the algorithm in createResource can continue checking with the 
+     * next registered ResourceLoader. 
+     */
+    protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
+            String resourceName, String libraryName, String localePrefix)
+    {
+        String resourceVersion = null;
+        String libraryVersion = null;
+        ResourceMeta resourceId = null;
+        
+        //1. Try to locate resource in a localized path
+        if (localePrefix != null)
+        {
+            if (null != libraryName)
+            {
+                String pathToLib = localePrefix + '/' + libraryName;
+                libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
+
+                if (null != libraryVersion)
+                {
+                    String pathToResource = localePrefix + '/'
+                            + libraryName + '/' + libraryVersion + '/'
+                            + resourceName;
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource);
+                }
+                else
+                {
+                    String pathToResource = localePrefix + '/'
+                            + libraryName + '/' + resourceName;
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource);
+                }
+
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {
+                    resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
+                            libraryVersion, resourceName, resourceVersion);
+                }
+            }
+            else
+            {
+                resourceVersion = resourceLoader
+                        .getResourceVersion(localePrefix + '/'+ resourceName);
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
+                            resourceName, resourceVersion);
+                }
+            }
+
+            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
+            {
+                resourceId = null;
+            }            
+        }
+        
+        //2. Try to localize resource in a non localized path
+        if (resourceId == null)
+        {
+            if (null != libraryName)
+            {
+                libraryVersion = resourceLoader.getLibraryVersion(libraryName);
+
+                if (null != libraryVersion)
+                {
+                    String pathToResource = (libraryName + '/' + libraryVersion
+                            + '/' + resourceName);
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource);
+                }
+                else
+                {
+                    String pathToResource = (libraryName + '/'
+                            + resourceName);
+                    resourceVersion = resourceLoader
+                            .getResourceVersion(pathToResource);
+                }
+
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(null, libraryName,
+                            libraryVersion, resourceName, resourceVersion);
+                }
+            }
+            else
+            {
+                resourceVersion = resourceLoader
+                        .getResourceVersion(resourceName);
+                
+                if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+                {               
+                    resourceId = resourceLoader.createResourceMeta(null, null, null,
+                            resourceName, resourceVersion);
+                }
+            }
+
+            if (resourceId != null && !resourceLoader.resourceExists(resourceId))
+            {
+                resourceId = null;
+            }            
+        }
+        
+        return resourceId;
+    }
+
+    @Override
+    public String getRendererTypeForResourceName(String resourceName)
+    {
+        if (resourceName.endsWith(".js"))
+        {
+            return "jakarta.faces.resource.Script";
+        }
+        else if (resourceName.endsWith(".css"))
+        {
+            return "jakarta.faces.resource.Stylesheet";
+        }
+        return null;
+    }
+
+    /**
+     *  Handle the resource request, writing in the output. 
+     *  
+     *  This method implements an algorithm semantically identical to 
+     *  the one described on the javadoc of ResourceHandler.handleResourceRequest 
+     */
+    @Override
+    public void handleResourceRequest(FacesContext facesContext) throws IOException
+    {
+        //try
+        //{
+            String resourceBasePath = getResourceHandlerSupport()
+                    .calculateResourceBasePath(facesContext);
+    
+            if (resourceBasePath == null)
+            {
+                // No base name could be calculated, so no further
+                //advance could be done here. HttpServletResponse.SC_NOT_FOUND
+                //cannot be returned since we cannot extract the 
+                //resource base name
+                return;
+            }
+    
+            // We neet to get an instance of HttpServletResponse, but sometimes
+            // the response object is wrapped by several instances of 
+            // ServletResponseWrapper (like ResponseSwitch).
+            // Since we are handling a resource, we can expect to get an 
+            // HttpServletResponse.
+            ExternalContext extContext = facesContext.getExternalContext();
+            Object response = extContext.getResponse();
+            HttpServletResponse httpServletResponse = ExternalContextUtils.getHttpServletResponse(response);
+            if (httpServletResponse == null)
+            {
+                throw new IllegalStateException("Could not obtain an instance of HttpServletResponse.");
+            }
+    
+            if (isResourceIdentifierExcluded(facesContext, resourceBasePath))
+            {
+                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+    
+            String resourceName = null;
+            if (resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
+            {
+                resourceName = resourceBasePath
+                        .substring(ResourceHandler.RESOURCE_IDENTIFIER.length() + 1);
+                
+                if (resourceBasePath != null && !ResourceValidationUtils.isValidResourceName(resourceName))
+                {
+                    httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                    return;
+                }
+            }
+            else
+            {
+                //Does not have the conditions for be a resource call
+                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+    
+            String libraryName = facesContext.getExternalContext()
+                    .getRequestParameterMap().get("ln");
+    
+            if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
+                    libraryName, isAllowSlashesLibraryName()))
+            {
+                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+            
+            Resource resource = null;
+            if (libraryName != null)
+            {
+                //log.info("libraryName=" + libraryName);
+                resource = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName);
+            }
+            else
+            {
+                resource = facesContext.getApplication().getResourceHandler().createResource(resourceName);
+            }
+    
+            if (resource == null)
+            {
+                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+    
+            if (!resource.userAgentNeedsUpdate(facesContext))
+            {
+                httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                return;
+            }
+    
+            httpServletResponse.setContentType(_getContentType(resource, facesContext.getExternalContext()));
+    
+            Map<String, String> headers = resource.getResponseHeaders();
+    
+            for (Map.Entry<String, String> entry : headers.entrySet())
+            {
+                httpServletResponse.setHeader(entry.getKey(), entry.getValue());
+            }
+    
+            // Sets the preferred buffer size for the body of the response
+            extContext.setResponseBufferSize(this.getResourceBufferSize());
+            
+            //serve up the bytes (taken from trinidad ResourceServlet)
+            try
+            {
+                InputStream in = resource.getInputStream();
+                OutputStream out = httpServletResponse.getOutputStream();
+                //byte[] buffer = new byte[_BUFFER_SIZE];
+                byte[] buffer = new byte[this.getResourceBufferSize()];
+    
+                try
+                {
+                    int count = pipeBytes(in, out, buffer);
+                    //set the content lenght
+                    if (!httpServletResponse.isCommitted())
+                    {
+                        httpServletResponse.setContentLength(count);
+                    }
+                }
+                finally
+                {
+                    try
+                    {
+                        in.close();
+                    }
+                    finally
+                    {
+                        out.close();
+                    }
+                }
+            }
+            catch (IOException e)
+            {
+                //TODO: Log using a localized message (which one?)
+                if (isConnectionAbort(e))
+                {
+                    log.log(Level.INFO,"Connection was aborted while loading resource " + resourceName
+                            + " with library " + libraryName);
+                }
+                else
+                {
+                    if (log.isLoggable(Level.WARNING))
+                    {
+                        log.log(Level.WARNING,"Error trying to load and send resource " + resourceName
+                                + " with library " + libraryName + " :"
+                                + e.getMessage(), e);
+                    }
+                    httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                }
+            }
+        //}
+        //catch (Throwable ex)
+        //{
+            // handle the Throwable accordingly. Maybe generate an error page.
+            // FIXME we are creating a html error page for a non html request here
+            // shouln't we do something better? -=Jakob Korherr=-
+            //ErrorPageWriter.handleThrowable(facesContext, ex);
+        //}
+    }
+
+    private static boolean isConnectionAbort(IOException e)
+    {
+        return e.getClass().getCanonicalName().equals("org.apache.catalina.connector.ClientAbortException");
+    }
+
+    /**
+     * Reads the specified input stream into the provided byte array storage and
+     * writes it to the output stream.
+     */
+    private static int pipeBytes(InputStream in, OutputStream out, byte[] buffer)
+            throws IOException
+    {
+        int count = 0;
+        int length;
+
+        while ((length = (in.read(buffer))) >= 0)
+        {
+            out.write(buffer, 0, length);
+            count += length;
+        }
+        return count;
+    }
+
+    @Override
+    public boolean isResourceRequest(FacesContext facesContext)
+    {
+        // Since this method could be called many times we save it
+        // on request map so the first time is calculated it remains
+        // alive until the end of the request
+        Boolean value = (Boolean) facesContext.getAttributes().get(IS_RESOURCE_REQUEST);
+
+        if (value == null)
+        {
+            String resourceBasePath = getResourceHandlerSupport()
+                    .calculateResourceBasePath(facesContext);
+
+            value = resourceBasePath != null
+                    && resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER);
+            facesContext.getAttributes().put(IS_RESOURCE_REQUEST, value);
+        }
+        return value;
+    }
+
+    protected String getLocalePrefixForLocateResource()
+    {
+        return getLocalePrefixForLocateResource(FacesContext.getCurrentInstance());
+    }
+
+    protected String getLocalePrefixForLocateResource(FacesContext context)
+    {
+        String localePrefix = null;
+        boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
+
+        if (isResourceRequest)
+        {
+            localePrefix = context.getExternalContext().getRequestParameterMap().get("loc");
+            
+            if (localePrefix != null)
+            {
+                if (!ResourceValidationUtils.isValidLocalePrefix(localePrefix))
+                {
+                    return null;
+                }
+                return localePrefix;
+            }
+        }
+        
+        String bundleName = context.getApplication().getMessageBundle();
+
+        if (null != bundleName)
+        {
+            Locale locale = null;
+            
+            if (isResourceRequest || context.getViewRoot() == null)
+            {
+                locale = context.getApplication().getViewHandler()
+                                .calculateLocale(context);
+            }
+            else
+            {
+                locale = context.getViewRoot().getLocale();
+            }
+
+            try
+            {
+                ResourceBundle bundle = ResourceBundle
+                        .getBundle(bundleName, locale, ClassUtils.getContextClassLoader());
+
+                if (bundle != null)
+                {
+                    if (bundle.containsKey(ResourceHandler.LOCALE_PREFIX))
+                    {
+                        localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
+                    }
+                }
+            }
+            catch (MissingResourceException e)
+            {
+                // Ignore it and return null
+            }
+        }
+        return localePrefix;
+    }
+    
+    protected String getContractNameForLocateResource(FacesContext context)
+    {
+        String contractName = null;
+        boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
+
+        if (isResourceRequest)
+        {
+            contractName = context.getExternalContext().getRequestParameterMap().get("con");
+        }
+        
+        // Check if the contract has been injected.
+        if (contractName == null)
+        {
+            contractName = (String) context.getAttributes().get(ContractResource.CONTRACT_SELECTED);
+        }
+        
+        //Validate
+        if (contractName != null &&
+            !ResourceValidationUtils.isValidContractName(contractName))
+        {
+            return null;
+        }
+        return contractName;
+    }
+
+    protected boolean isResourceIdentifierExcluded(FacesContext context, String resourceIdentifier)
+    {
+        if (_excludedResourceExtensions == null)
+        {
+            String value = WebConfigParamUtils.getStringInitParameter(context.getExternalContext(),
+                            RESOURCE_EXCLUDES_PARAM_NAME,
+                            RESOURCE_EXCLUDES_DEFAULT_VALUE);
+            
+            _excludedResourceExtensions = StringUtils.splitShortString(value, ' ');
+        }
+        
+        for (int i = 0; i < _excludedResourceExtensions.length; i++)
+        {
+            if (resourceIdentifier.endsWith(_excludedResourceExtensions[i]))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if a library exists or not. This is done delegating
+     * to each ResourceLoader used, because each one has a different
+     * prefix and way to load resources.
+     * 
+     */
+    @Override
+    public boolean libraryExists(String libraryName)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        String localePrefix = getLocalePrefixForLocateResource(facesContext);
+        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
+
+        String pathToLib = null;
+        Boolean libraryFound = null;
+        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
+                libraryName, isAllowSlashesLibraryName()))
+        {
+            return false;
+        }
+        
+        if (localePrefix != null)
+        {
+            //Check with locale
+            pathToLib = localePrefix + '/' + libraryName;
+
+            libraryFound = getResourceLoaderCache().libraryExists(pathToLib);
+            if (libraryFound != null)
+            {
+                return libraryFound.booleanValue();
+            }
+        }
+        libraryFound = getResourceLoaderCache().libraryExists(libraryName);
+        if (libraryFound != null)
+        {
+            return libraryFound.booleanValue();
+        }
+        
+        if (localePrefix != null)
+        {
+            if (!contracts.isEmpty())
+            {
+                for (String contract : contracts)
+                {
+                    for (ContractResourceLoader loader : getResourceHandlerSupport()
+                            .getContractResourceLoaders())
+                    {
+                        if (loader.libraryExists(pathToLib, contract))
+                        {
+                            getResourceLoaderCache().confirmLibraryExists(pathToLib);
+                            return true;
+                        }
+                    }
+                }
+            }
+            
+            for (ResourceLoader loader : getResourceHandlerSupport()
+                    .getResourceLoaders())
+            {
+                if (loader.libraryExists(pathToLib))
+                {
+                    getResourceLoaderCache().confirmLibraryExists(pathToLib);
+                    return true;
+                }
+            }            
+        }
+
+        //Check without locale
+        if (!contracts.isEmpty())
+        {
+            for (String contract : contracts)
+            {
+                for (ContractResourceLoader loader : getResourceHandlerSupport()
+                        .getContractResourceLoaders())
+                {
+                    if (loader.libraryExists(libraryName, contract))
+                    {
+                        getResourceLoaderCache().confirmLibraryExists(libraryName);
+                        return true;
+                    }
+                }
+            }
+        }
+
+        for (ResourceLoader loader : getResourceHandlerSupport()
+                .getResourceLoaders())
+        {
+            if (loader.libraryExists(libraryName))
+            {
+                getResourceLoaderCache().confirmLibraryExists(libraryName);
+                return true;
+            }
+        }
+
+        if (localePrefix != null)
+        {
+            //Check with locale
+            getResourceLoaderCache().confirmLibraryNotExists(pathToLib);
+        }
+        else
+        {
+            getResourceLoaderCache().confirmLibraryNotExists(libraryName);
+        }
+        return false;
+    }
+
+    /**
+     * @param resourceHandlerSupport
+     *            the resourceHandlerSupport to set
+     */
+    public void setResourceHandlerSupport(
+            ResourceHandlerSupport resourceHandlerSupport)
+    {
+        _resourceHandlerSupport = resourceHandlerSupport;
+    }
+
+    /**
+     * @return the resourceHandlerSupport
+     */
+    protected ResourceHandlerSupport getResourceHandlerSupport()
+    {
+        if (_resourceHandlerSupport == null)
+        {
+            _resourceHandlerSupport = new DefaultResourceHandlerSupport();
+        }
+        return _resourceHandlerSupport;
+    }
+
+    private ResourceHandlerCache getResourceLoaderCache()
+    {
+        if (_resourceHandlerCache == null)
+        {
+            _resourceHandlerCache = new ResourceHandlerCache();
+        }
+        return _resourceHandlerCache;
+    }
+
+    private String _getContentType(Resource resource, ExternalContext externalContext)
+    {
+        String contentType = resource.getContentType();
+
+        // the resource does not provide a content-type --> determine it via mime-type
+        if (contentType == null || contentType.length() == 0)
+        {
+            String resourceName = getWrappedResourceName(resource);
+
+            if (resourceName != null)
+            {
+                contentType = externalContext.getMimeType(resourceName);
+            }
+        }
+
+        return contentType;
+    }
+
+    /**
+     * Recursively unwarp the resource until we find the real resourceName
+     * This is needed because the JSF2 specced ResourceWrapper doesn't override
+     * the getResourceName() method :(
+     * @param resource
+     * @return the first non-null resourceName or <code>null</code> if none set
+     */
+    private String getWrappedResourceName(Resource resource)
+    {
+        String resourceName = resource.getResourceName();
+        if (resourceName != null)
+        {
+            return resourceName;
+        }
+
+        if (resource instanceof ResourceWrapper)
+        {
+            return getWrappedResourceName(((ResourceWrapper) resource).getWrapped());
+        }
+
+        return null;
+    }
+    
+    protected boolean isAllowSlashesLibraryName()
+    {
+        if (_allowSlashLibraryName == null)
+        {
+            _allowSlashLibraryName = WebConfigParamUtils.getBooleanInitParameter(
+                    FacesContext.getCurrentInstance().getExternalContext(), 
+                    INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME,
+                    INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT);
+        }
+        return _allowSlashLibraryName;
+    }
+
+    protected int getResourceBufferSize()
+    {
+        if (_resourceBufferSize == -1)
+        {
+            _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
+                FacesContext.getCurrentInstance().getExternalContext(),
+                INIT_PARAM_RESOURCE_BUFFER_SIZE,
+                INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
+        }
+        return _resourceBufferSize;
+    }
+
+    @Override
+    public Resource createResourceFromId(String resourceId)
+    {
+        Resource resource = null;
+
+        if (resourceId == null)
+        {
+            throw new NullPointerException();
+        }
+        
+        // Later in deriveResourceMeta the resourceId is decomposed and
+        // its elements validated properly.
+        if (!ResourceValidationUtils.isValidResourceId(resourceId))
+        {
+            return null;
+        }
+        
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
+        String contractPreferred = getContractNameForLocateResource(facesContext);
+        ResourceValue resourceValue = null;
+        
+        // Check cache:
+        //
+        // Contracts are on top of everything, because it is a concept that defines
+        // resources in a application scope concept. It means all resources in
+        // /resources or /META-INF/resources can be overriden using a contract. Note
+        // it also means resources under /META-INF/flows can also be overriden using
+        // a contract.
+        if (contractPreferred != null)
+        {
+            resourceValue = getResourceLoaderCache().getResource(
+                    resourceId, contractPreferred);
+        }
+        if (resourceValue == null && !contracts.isEmpty())
+        {
+            // Try to get resource but try with a contract name
+            for (String contract : contracts)
+            {
+                resourceValue = getResourceLoaderCache().getResource(resourceId, contract);
+                if (resourceValue != null)
+                {
+                    break;
+                }
+            }
+        }
+        if (resourceValue == null)
+        {
+            // Try to get resource without contract name
+            resourceValue = getResourceLoaderCache().getResource(resourceId);
+        }
+        
+        if(resourceValue != null)
+        {        
+            //Resolve contentType using ExternalContext.getMimeType
+            String contentType = facesContext.getExternalContext().getMimeType(
+                resourceValue.getResourceMeta().getResourceName());
+
+            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
+                    getResourceHandlerSupport(), contentType,
+                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
+                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
+        }
+        else
+        {
+            boolean resolved = false;
+            if (contractPreferred != null)
+            {
+                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveResourceMeta(
+                        facesContext, loader, resourceId, contractPreferred);
+                    if (resourceMeta != null)
+                    {
+                        String contentType = facesContext.getExternalContext().getMimeType(
+                            resourceMeta.getResourceName());
+                        
+                        resource = new ResourceImpl(resourceMeta, loader, 
+                            getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
+                            new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+                        
+                        resolved = true;
+                        break;
+                    }
+                }
+            }
+            if (!resolved && !contracts.isEmpty())
+            {
+                for (ContractResourceLoader loader : 
+                        getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    for (String contract : contracts)
+                    {
+                        ResourceMeta resourceMeta = deriveResourceMeta(
+                            facesContext, loader, resourceId, contract);
+                        if (resourceMeta != null)
+                        {
+                            String contentType = facesContext.getExternalContext().getMimeType(
+                                resourceMeta.getResourceName());
+
+                            resource = new ResourceImpl(resourceMeta, loader, 
+                                getResourceHandlerSupport(), contentType);
+
+                            // cache it
+                            getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
+                                new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+
+                            resolved = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!resolved)
+            {
+                for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveResourceMeta(facesContext, loader, resourceId);
+
+                    if (resourceMeta != null)
+                    {
+                        String contentType = facesContext.getExternalContext().getMimeType(
+                            resourceMeta.getResourceName());
+
+                        resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
+                            new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
+                        break;
+                    }
+                }
+            }
+        }
+        return resource;
+    }
+
+    protected ResourceMeta deriveResourceMeta(FacesContext context, ResourceLoader resourceLoader,
+            String resourceId)
+    {
+        ResourceMeta resourceMeta = null;
+        String token = null;
+        String localePrefix = null;
+        String libraryName = null;
+        String libraryVersion = null;
+        String resourceName = null;
+        String resourceVersion = null;
+        
+        // Check if resource exists. It avoids additional 
+        // checks and it can be done very quickly because the 
+        // loader always uses the resourceId structure to
+        // organize resources. But decompose the resourceId is
+        // even faster.
+        //if (resourceLoader.resourceIdExists(resourceId))
+        //{
+        int lastSlash = resourceId.lastIndexOf('/');
+        if (lastSlash < 0)
+        {
+            //no slashes, so it is just a plain resource.
+            resourceName = resourceId;
+        }
+        else
+        {
+            token = resourceId.substring(lastSlash+1);
+            if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
+            {
+                int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
+                if (secondLastSlash < 0)
+                {
+                    secondLastSlash = 0;
+                }
+
+                String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
+                int lastPoint = rnToken.lastIndexOf('.');
+                // lastPoint < 0 means it does not match, the token is not a resource version
+                if (lastPoint >= 0)
+                {
+                    String ext = rnToken.substring(lastPoint);
+                    if (token.endsWith(ext))
+                    {
+                        //It match a versioned resource
+                        resourceVersion = token.substring(0,token.length()-ext.length());
+                    }
+                }
+            }
+
+            // 1. Extract the library path and locale prefix if necessary
+            int start = 0;
+            int firstSlash = resourceId.indexOf('/');
+
+            // At least one slash, check if the start is locale prefix.
+            String bundleName = context.getApplication().getMessageBundle();
+            //If no bundle set, it can't be localePrefix
+            if (null != bundleName)
+            {
+                token = resourceId.substring(start, firstSlash);
+                //Try to derive a locale object
+                Locale locale = _LocaleUtils.deriveLocale(token);
+
+                // If the locale was derived and it is available, 
+                // assume that portion of the resourceId it as a locale prefix.
+                if (locale != null && _LocaleUtils.isAvailableLocale(locale))
+                {
+                    localePrefix = token;
+                    start = firstSlash+1;
+                }
+            }
+
+            //Check slash again from start
+            firstSlash = resourceId.indexOf('/', start);
+            if (firstSlash < 0)
+            {
+                //no slashes.
+                resourceName = resourceId.substring(start);
+            }
+            else
+            {
+                //check libraryName
+                token = resourceId.substring(start, firstSlash);
+                int minResourceNameSlash = (resourceVersion != null) ?
+                    resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
+                //if (resourceLoader.libraryExists(token))
+                if (start < minResourceNameSlash)
+                {
+                    libraryName = token;
+                    start = firstSlash+1;
+
+                    //Now that libraryName exists, check libraryVersion
+                    firstSlash = resourceId.indexOf('/', start);
+                    if (firstSlash >= 0)
+                    {
+                        token = resourceId.substring(start, firstSlash);
+                        if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
+                        {
+                            libraryVersion = token;
+                            start = firstSlash+1;
+                        }
+                    }
+                }
+
+                firstSlash = resourceId.indexOf('/', start);
+                if (firstSlash < 0)
+                {
+                    //no slashes.
+                    resourceName = resourceId.substring(start);
+                }
+                else
+                {
+                    // Check resource version. 
+                    if (resourceVersion != null)
+                    {
+                        resourceName = resourceId.substring(start,lastSlash);
+                    }
+                    else
+                    {
+                        //no resource version, assume the remaining to be resource name
+                        resourceName = resourceId.substring(start);
+                    }
+                }
+            }
+        }
+
+        //Check libraryName and resourceName
+        if (resourceName == null)
+        {
+            return null;
+        }
+        if (!ResourceValidationUtils.isValidResourceName(resourceName))
+        {
+            return null;
+        }
+
+        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
+                libraryName, isAllowSlashesLibraryName()))
+        {
+            return null;
+        }
+
+        // If some variable is "" set it as null.
+        if (localePrefix != null && localePrefix.length() == 0)
+        {
+            localePrefix = null;
+        }
+        if (libraryName != null && libraryName.length() == 0)
+        {
+            libraryName = null;
+        }
+        if (libraryVersion != null && libraryVersion.length() == 0)
+        {
+            libraryVersion = null;
+        }
+        if (resourceName != null && resourceName.length() == 0)
+        {
+            resourceName = null;
+        }
+        if (resourceVersion != null && resourceVersion.length() == 0)
+        {
+            resourceVersion = null;
+        }
+
+        resourceMeta = resourceLoader.createResourceMeta(
+            localePrefix, libraryName, libraryVersion, resourceName, resourceVersion);
+
+        if (resourceMeta != null &&
+            !resourceLoader.resourceExists(resourceMeta))
+        {
+            resourceMeta = null;
+        }
+        //}
+        return resourceMeta;
+    }
+    
+    protected ResourceMeta deriveResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
+            String resourceId, String contractName)
+    {
+        ResourceMeta resourceMeta = null;
+        String token = null;
+        String localePrefix = null;
+        String libraryName = null;
+        String libraryVersion = null;
+        String resourceName = null;
+        String resourceVersion = null;
+        
+        // Check if resource exists. It avoids additional 
+        // checks and it can be done very quickly because the 
+        // loader always uses the resourceId structure to
+        // organize resources. But decompose the resourceId is
+        // even faster.
+        //if (resourceLoader.resourceIdExists(resourceId))
+        //{
+        int lastSlash = resourceId.lastIndexOf('/');
+        if (lastSlash < 0)
+        {
+            //no slashes, so it is just a plain resource.
+            resourceName = resourceId;
+        }
+        else
+        {
+            token = resourceId.substring(lastSlash+1);
+            if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
+            {
+                int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
+                if (secondLastSlash < 0)
+                {
+                    secondLastSlash = 0;
+                }
+
+                String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
+                int lastPoint = rnToken.lastIndexOf('.');
+                // lastPoint < 0 means it does not match, the token is not a resource version
+                if (lastPoint >= 0)
+                {
+                    String ext = rnToken.substring(lastPoint);
+                    if (token.endsWith(ext))
+                    {
+                        //It match a versioned resource
+                        resourceVersion = token.substring(0,token.length()-ext.length());
+                    }
+                }
+            }
+
+            // 1. Extract the library path and locale prefix if necessary
+            int start = 0;
+            int firstSlash = resourceId.indexOf('/');
+
+            // At least one slash, check if the start is locale prefix.
+            String bundleName = context.getApplication().getMessageBundle();
+            //If no bundle set, it can't be localePrefix
+            if (null != bundleName)
+            {
+                token = resourceId.substring(start, firstSlash);
+                //Try to derive a locale object
+                Locale locale = _LocaleUtils.deriveLocale(token);
+
+                // If the locale was derived and it is available, 
+                // assume that portion of the resourceId it as a locale prefix.
+                if (locale != null && _LocaleUtils.isAvailableLocale(locale))
+                {
+                    localePrefix = token;
+                    start = firstSlash+1;
+                }
+            }
+
+            //Check slash again from start
+            firstSlash = resourceId.indexOf('/', start);
+            if (firstSlash < 0)
+            {
+                //no slashes.
+                resourceName = resourceId.substring(start);
+            }
+            else
+            {
+                //check libraryName
+                token = resourceId.substring(start, firstSlash);
+                int minResourceNameSlash = (resourceVersion != null) ?
+                    resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
+                //if (resourceLoader.libraryExists(token))
+                if (start < minResourceNameSlash)
+                {
+                    libraryName = token;
+                    start = firstSlash+1;
+
+                    //Now that libraryName exists, check libraryVersion
+                    firstSlash = resourceId.indexOf('/', start);
+                    if (firstSlash >= 0)
+                    {
+                        token = resourceId.substring(start, firstSlash);
+                        if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
+                        {
+                            libraryVersion = token;
+                            start = firstSlash+1;
+                        }
+                    }
+                }
+
+                firstSlash = resourceId.indexOf('/', start);
+                if (firstSlash < 0)
+                {
+                    //no slashes.
+                    resourceName = resourceId.substring(start);
+                }
+                else
+                {
+                    // Check resource version. 
+                    if (resourceVersion != null)
+                    {
+                        resourceName = resourceId.substring(start,lastSlash);
+                    }
+                    else
+                    {
+                        //no resource version, assume the remaining to be resource name
+                        resourceName = resourceId.substring(start);
+                    }
+                }
+            }
+        }
+
+        //Check libraryName and resourceName
+        if (resourceName == null)
+        {
+            return null;
+        }
+        if (!ResourceValidationUtils.isValidResourceName(resourceName))
+        {
+            return null;
+        }
+
+        if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
+                libraryName, isAllowSlashesLibraryName()))
+        {
+            return null;
+        }
+
+        // If some variable is "" set it as null.
+        if (localePrefix != null && localePrefix.length() == 0)
+        {
+            localePrefix = null;
+        }
+        if (libraryName != null && libraryName.length() == 0)
+        {
+            libraryName = null;
+        }
+        if (libraryVersion != null && libraryVersion.length() == 0)
+        {
+            libraryVersion = null;
+        }
+        if (resourceName != null && resourceName.length() == 0)
+        {
+            resourceName = null;
+        }
+        if (resourceVersion != null && resourceVersion.length() == 0)
+        {
+            resourceVersion = null;
+        }
+
+        resourceMeta = resourceLoader.createResourceMeta(
+            localePrefix, libraryName, libraryVersion, resourceName, resourceVersion, contractName);
+
+        if (resourceMeta != null &&
+            !resourceLoader.resourceExists(resourceMeta))
+        {
+            resourceMeta = null;
+        }
+        //}
+        return resourceMeta;
+    }
+    
+    protected ResourceMeta deriveViewResourceMeta(FacesContext context, ResourceLoader resourceLoader,
+            String resourceName, String localePrefix)
+    {
+        ResourceMeta resourceMeta = null;
+        String resourceVersion = null;
+
+        //1. Try to locate resource in a localized path
+        if (localePrefix != null)
+        {
+            resourceVersion = resourceLoader
+                    .getResourceVersion(localePrefix + '/'+ resourceName);
+            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+            {
+                resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
+                         resourceName, resourceVersion);
+            }
+
+            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
+            {
+                resourceMeta = null;
+            }            
+        }
+        
+        //2. Try to localize resource in a non localized path
+        if (resourceMeta == null)
+        {
+            resourceVersion = resourceLoader
+                    .getResourceVersion(resourceName);
+            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+            {
+                resourceMeta = resourceLoader.createResourceMeta(null, null, null,
+                         resourceName, resourceVersion);
+            }
+
+            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
+            {
+                resourceMeta = null;
+            }            
+        }
+
+        return resourceMeta;        
+    }
+    
+    protected ResourceMeta deriveViewResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
+            String resourceName, String localePrefix, String contractName)
+    {
+        ResourceMeta resourceMeta = null;
+        String resourceVersion = null;
+
+        //1. Try to locate resource in a localized path
+        if (localePrefix != null)
+        {
+            resourceVersion = resourceLoader
+                    .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
+            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+            {
+                resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
+                     resourceName, resourceVersion, contractName);
+            }
+
+            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
+            {
+                resourceMeta = null;
+            }            
+        }
+        
+        //2. Try to localize resource in a non localized path
+        if (resourceMeta == null)
+        {
+            resourceVersion = resourceLoader
+                    .getResourceVersion(resourceName, contractName);
+            if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+            {
+                resourceMeta = resourceLoader.createResourceMeta(null, null, null,
+                         resourceName, resourceVersion, contractName);
+            }
+
+            if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
+            {
+                resourceMeta = null;
+            }            
+        }
+
+        return resourceMeta;
+    }
+
+    @Override
+    public Resource createViewResource(FacesContext facesContext, String resourceName)
+    {
+        // There are some special points to remember for a view resource in comparison
+        // with a normal resource:
+        //
+        // - A view resource never has an associated library name 
+        //   (this was done to keep simplicity).
+        // - A view resource can be inside a resource library contract.
+        // - A view resource could be internationalized in the same way a normal resource.
+        // - A view resource can be created from the webapp root folder, 
+        //   a normal resource cannot.
+        // - A view resource cannot be created from /resources or META-INF/resources.
+        // 
+        // For example, a valid resourceId for a view resource is like this:
+        //
+        // [localePrefix/]resourceName[/resourceVersion]
+        //
+        // but the resource loader can ignore localePrefix or resourceVersion, like
+        // for example the webapp root folder.
+        // 
+        // When createViewResource() is called, the view must be used to derive
+        // the localePrefix and facesContext must be used to get the available contracts.
+        
+        Resource resource = null;
+
+        if (resourceName == null)
+        {
+            throw new NullPointerException();
+        }
+        if (resourceName.charAt(0) == '/')
+        {
+            // If resourceName starts with '/', remove that character because it
+            // does not have any meaning (with and without should point to the 
+            // same resource).
+            resourceName = resourceName.substring(1);
+        }
+        
+        // Later in deriveResourceMeta the resourceId is decomposed and
+        // its elements validated properly.
+        if (!ResourceValidationUtils.isValidViewResource(resourceName))
+        {
+            return null;
+        }
+        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
+        String contentType = facesContext.getExternalContext().getMimeType(resourceName);
+        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
+        String contractPreferred = getContractNameForLocateResource(facesContext);
+        ResourceValue resourceValue = null;
+        
+        // Check cache:
+        //
+        // Contracts are on top of everything, because it is a concept that defines
+        // resources in a application scope concept. It means all resources in
+        // /resources or /META-INF/resources can be overriden using a contract. Note
+        // it also means resources under /META-INF/flows can also be overriden using
+        // a contract.
+        if (contractPreferred != null)
+        {
+            resourceValue = getResourceLoaderCache().getViewResource(
+                    resourceName, contentType, localePrefix, contractPreferred);
+        }
+        if (resourceValue == null && !contracts.isEmpty())
+        {
+            // Try to get resource but try with a contract name
+            for (String contract : contracts)
+            {
+                resourceValue = getResourceLoaderCache().getViewResource(
+                    resourceName, contentType, localePrefix, contract);
+                if (resourceValue != null)
+                {
+                    break;
+                }
+            }
+        }
+        if (resourceValue == null)
+        {
+            // Try to get resource without contract name
+            resourceValue = getResourceLoaderCache().getViewResource(
+                resourceName, contentType, localePrefix);
+        }
+
+        if(resourceValue != null)
+        {        
+            resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
+                    getResourceHandlerSupport(), contentType, 
+                    resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, null);
+        }
+        else
+        {
+            boolean resolved = false;
+            if (contractPreferred != null)
+            {
+                for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveViewResourceMeta(
+                        facesContext, loader, resourceName, localePrefix, contractPreferred);
+                    if (resourceMeta != null)
+                    {
+                        resource = new ResourceImpl(resourceMeta, loader, 
+                            getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putViewResource(
+                            resourceName, contentType, localePrefix, contractPreferred, resourceMeta, loader, 
+                            new ResourceCachedInfo(resource.getURL(), null));
+                        
+                        resolved = true;
+                        break;
+                    }
+                }
+            }
+            if (!resolved && !contracts.isEmpty())
+            {
+                for (ContractResourceLoader loader : 
+                        getResourceHandlerSupport().getContractResourceLoaders())
+                {
+                    for (String contract : contracts)
+                    {
+                        ResourceMeta resourceMeta = deriveViewResourceMeta(
+                            facesContext, loader, resourceName, localePrefix, contract);
+                        if (resourceMeta != null)
+                        {
+                            resource = new ResourceImpl(resourceMeta, loader, 
+                                getResourceHandlerSupport(), contentType);
+
+                            // cache it
+                            getResourceLoaderCache().putViewResource(
+                                resourceName, contentType, localePrefix, contract, resourceMeta, loader,
+                                new ResourceCachedInfo(resource.getURL(), null));
+
+                            resolved = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!resolved)
+            {
+                // "... Considering the web app root ..."
+                
+                // "... Considering faces flows (at the locations specified in the spec prose document section 
+                // Faces Flows in the Using JSF in Web Applications chapter) ..."
+                for (ResourceLoader loader : getResourceHandlerSupport().getViewResourceLoaders())
+                {
+                    ResourceMeta resourceMeta = deriveViewResourceMeta(
+                        facesContext, loader, resourceName, localePrefix);
+
+                    if (resourceMeta != null)
+                    {
+                        resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
+
+                        // cache it
+                        getResourceLoaderCache().putViewResource(
+                            resourceName, contentType, localePrefix, resourceMeta, loader,
+                            new ResourceCachedInfo(resource.getURL(), null));
+                        break;
+                    }
+                }
+            }
+        }
+        return resource;
+    }
+
+    public Stream<java.lang.String> getViewResources(FacesContext facesContext, 
+            String path, int maxDepth, ResourceVisitOption... options)   
+    {
+        final String localePrefix = getLocalePrefixForLocateResource(facesContext);
+        final List<String> contracts = facesContext.getResourceLibraryContracts(); 
+        String contractPreferred = getContractNameForLocateResource(facesContext);
+
+        if (this._viewSuffixes == null)
+        {
+            this._viewSuffixes = loadSuffixes(facesContext.getExternalContext());
+        }
+
+        Iterator it = new FilterInvalidSuffixViewResourceIterator(new ViewResourceIterator(facesContext, 
+                    getResourceHandlerSupport(), localePrefix, contracts,
+                    contractPreferred, path, maxDepth, options), facesContext, _viewSuffixes);
+ 
+        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,Spliterator.DISTINCT), false);
+    }
+    
+    private Set<String> loadSuffixes (ExternalContext context)
+    {
+        Set<String> result = new HashSet<String>();
+        String definedSuffixes = WebConfigParamUtils.getStringInitParameter(context, 
+                ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX);
+        StringTokenizer tokenizer;
+        
+        if (definedSuffixes == null) 
+        {
+            definedSuffixes = ViewHandler.DEFAULT_SUFFIX;
+        }
+        
+        // This is a space-separated list of suffixes, so parse them out.
+        
+        tokenizer = new StringTokenizer (definedSuffixes, " ");
+        
+        while (tokenizer.hasMoreTokens()) 
+        {
+            result.add (tokenizer.nextToken());
+        }
+        
+        String faceletSuffix = WebConfigParamUtils.getStringInitParameter(context, 
+                ViewHandler.FACELETS_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_FACELETS_SUFFIX);
+        
+        if (faceletSuffix != null)
+        {
+            result.add(faceletSuffix.trim());
+        }
+        
+        String faceletViewMappings = WebConfigParamUtils.getStringInitParameter(context, FACELETS_VIEW_MAPPINGS_PARAM);
+        
+        if (faceletViewMappings != null)
+        {
+            tokenizer = new StringTokenizer(faceletViewMappings, ";");
+            while (tokenizer.hasMoreTokens()) 
+            {
+                result.add (tokenizer.nextToken());
+            }
+        }
+        
+        return result;
+    }    
+    
+    /*
+     * Filter out views without a valid suffix.
+     */
+    private static class FilterInvalidSuffixViewResourceIterator extends SkipMatchIterator<String>
+    {
+        private FacesContext facesContext;
+        private Set<String> validSuffixes;
+
+        public FilterInvalidSuffixViewResourceIterator(Iterator<String> delegate, FacesContext facesContext,
+                Set<String> validSuffixes)
+        {
+            super(delegate);
+            this.facesContext = facesContext;
+            this.validSuffixes = validSuffixes;
+        }
+
+        @Override
+        protected boolean match(String value)
+        {
+            String viewId = (String) value;
+            ViewDeclarationLanguage vdl = facesContext.getApplication().getViewHandler()
+                    .getViewDeclarationLanguage(facesContext, viewId);
+            if (vdl != null && vdl.viewExists(facesContext, viewId))
+            {
+                boolean matchSuffix = false;
+                for (String suffix : validSuffixes)
+                {
+                    if (suffix != null && suffix.length() > 0 && viewId.endsWith(suffix))
+                    {
+                        matchSuffix = true;
+                        break;
+                    }
+                }
+                if (matchSuffix)
+                {
+                    //There is view, do not match
+                    return false;
+                }
+                else
+                {
+                    return true;
+                }
+            }
+            // It is another resource file, skip
+            return true;
+        }
+    }
+
+    /**
+     * @since 2.3
+     * @param facesContext
+     * @param resourceName
+     * @param libraryName
+     * @return 
+     */
+    public boolean isResourceRendered(FacesContext facesContext, String resourceName, String libraryName)
+    {
+        return getRenderedResources(facesContext).containsKey(
+                libraryName != null
+                        ? contactLibraryAndResource(facesContext, libraryName, resourceName)
+                        : resourceName);
+    }
+
+    /**
+     * @since 2.3
+     * @param facesContext
+     * @param resourceName
+     * @param libraryName 
+     */
+    @Override
+    public void markResourceRendered(FacesContext facesContext, String resourceName, String libraryName)
+    {
+        getRenderedResources(facesContext).put(
+                libraryName != null
+                        ? contactLibraryAndResource(facesContext, libraryName, resourceName)
+                        : resourceName,
+                Boolean.TRUE);
+
+        if (ResourceHandler.JSF_SCRIPT_LIBRARY_NAME.equals(libraryName) &&
+            ResourceHandler.JSF_SCRIPT_RESOURCE_NAME.equals(resourceName))
+        {
+            // If we are calling this method, it is expected myfaces core is being used as runtime and note
+            // oamSubmit script is included inside jsf.js, so mark this one too.
+            getRenderedResources(facesContext).put(
+                    contactLibraryAndResource(facesContext, MYFACES_LIBRARY_NAME, MYFACES_JS_RESOURCE_NAME),
+                    Boolean.TRUE);
+        }
+    }
+    
+    /**
+     * Return a set of already rendered resources by this renderer on the current
+     * request. 
+     * 
+     * @param facesContext
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    private static Map<String, Boolean> getRenderedResources(FacesContext facesContext)
+    {
+        Map<String, Boolean> map = (Map<String, Boolean>) facesContext.getViewRoot().getTransientStateHelper()
+                .getTransient(RENDERED_RESOURCES_SET);
+        if (map == null)
+        {
+            map = new HashMap<String, Boolean>();
+            facesContext.getViewRoot().getTransientStateHelper().putTransient(RENDERED_RESOURCES_SET,map);
+        }
+        return map;
+    }
+
+    private static String contactLibraryAndResource(FacesContext facesContext, String libraryName, String resourceName)
+    {       
+        StringBuilder sb = SharedStringBuilder.get(facesContext, SHARED_STRING_BUILDER, 40);
+        return sb.append(libraryName).append('/').append(resourceName).toString();
+    }
+}