| /* |
| * 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.renderkit.html.util.ResourceUtils; |
| import org.apache.myfaces.resource.ResourceHandlerCache; |
| import org.apache.myfaces.resource.ResourceHandlerCache.ResourceValue; |
| import org.apache.myfaces.resource.ResourceHandlerSupport; |
| import org.apache.myfaces.resource.ResourceImpl; |
| import org.apache.myfaces.resource.ResourceLoader; |
| import org.apache.myfaces.resource.ResourceMeta; |
| import org.apache.myfaces.resource.ResourceValidationUtils; |
| import org.apache.myfaces.util.lang.ClassUtils; |
| import org.apache.myfaces.util.ExternalContextUtils; |
| import org.apache.myfaces.util.lang.StringUtils; |
| import org.apache.myfaces.util.WebConfigParamUtils; |
| |
| import javax.faces.application.Resource; |
| import javax.faces.application.ResourceHandler; |
| import javax.faces.application.ResourceWrapper; |
| import javax.faces.context.ExternalContext; |
| import javax.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 javax.faces.application.ResourceVisitOption; |
| import javax.faces.application.ViewHandler; |
| import javax.faces.view.ViewDeclarationLanguage; |
| import org.apache.myfaces.config.MyfacesConfig; |
| import org.apache.myfaces.util.SharedStringBuilder; |
| import org.apache.myfaces.resource.ContractResource; |
| import org.apache.myfaces.resource.ContractResourceLoader; |
| import org.apache.myfaces.resource.ResourceCachedInfo; |
| import org.apache.myfaces.util.lang.Assert; |
| import org.apache.myfaces.util.lang.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 static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName()); |
| |
| 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}*)*\\..*"); |
| |
| public final static String RENDERED_RESOURCES_SET = "org.apache.myfaces.RENDERED_RESOURCES_SET"; |
| |
| private static final String SHARED_STRING_BUILDER = ResourceHandlerImpl.class.getName() + ".SHARED_STRING_BUILDER"; |
| |
| private static final String[] FACELETS_VIEW_MAPPINGS_PARAM = {ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME, |
| "facelets.VIEW_MAPPINGS"}; |
| |
| private ResourceHandlerSupport _resourceHandlerSupport; |
| private ResourceHandlerCache _resourceHandlerCache; |
| private Boolean _allowSlashLibraryName; |
| private int _resourceBufferSize = -1; |
| private String[] _excludedResourceExtensions; |
| private Set<String> _viewSuffixes = null; |
| |
| @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) |
| { |
| Assert.notNull(resourceName, "resourceName"); |
| |
| Resource resource = null; |
| |
| 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 ResourceUtils.DEFAULT_SCRIPT_RENDERER_TYPE; |
| } |
| else if (resourceName.endsWith(".css")) |
| { |
| return ResourceUtils.DEFAULT_STYLESHEET_RENDERER_TYPE; |
| } |
| 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 |
| { |
| 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) |
| { |
| 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[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) |
| { |
| if (isConnectionAbort(e)) |
| { |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.log(Level.FINE, "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); |
| } |
| } |
| } |
| |
| private static boolean isConnectionAbort(IOException e) |
| { |
| return e.getClass().getCanonicalName().equals("org.apache.catalina.connector.ClientAbortException") |
| || e.getClass().getCanonicalName().equals("org.eclipse.jetty.io.EofException"); |
| } |
| |
| /** |
| * 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 (bundleName != null) |
| { |
| Locale locale = null; |
| |
| if (isResourceRequest || context.getViewRoot() == null) |
| { |
| locale = context.getApplication().getViewHandler().calculateLocale(context); |
| } |
| else |
| { |
| locale = context.getViewRoot().getLocale(); |
| } |
| |
| try |
| { |
| ResourceBundle bundle; |
| ResourceBundle.Control bundleControl = MyfacesConfig.getCurrentInstance(context) |
| .getResourceBundleControl(); |
| if (bundleControl == null) |
| { |
| bundle = ResourceBundle.getBundle(bundleName, locale, ClassUtils.getContextClassLoader()); |
| } |
| else |
| { |
| bundle = ResourceBundle.getBundle(bundleName, locale, ClassUtils.getContextClassLoader(), |
| bundleControl); |
| } |
| |
| if (bundle != null && 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; |
| } |
| } |
| libraryFound = getResourceLoaderCache().libraryExists(libraryName); |
| if (libraryFound != null) |
| { |
| return libraryFound; |
| } |
| |
| 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; |
| } |
| |
| public void setResourceHandlerSupport(ResourceHandlerSupport resourceHandlerSupport) |
| { |
| _resourceHandlerSupport = 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 = MyfacesConfig.getCurrentInstance().isStrictJsf2AllowSlashLibraryName(); |
| } |
| return _allowSlashLibraryName; |
| } |
| |
| protected int getResourceBufferSize() |
| { |
| if (_resourceBufferSize == -1) |
| { |
| _resourceBufferSize = MyfacesConfig.getCurrentInstance().getResourceBufferSize(); |
| } |
| return _resourceBufferSize; |
| } |
| |
| @Override |
| public Resource createResourceFromId(String resourceId) |
| { |
| Resource resource = null; |
| |
| Assert.notNull(resourceId, "resourceId"); |
| |
| // 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; |
| |
| 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 (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; |
| |
| 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 (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; |
| |
| Assert.notNull(resourceName, "resourceName"); |
| |
| 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; |
| } |
| |
| @Override |
| public Stream<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 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 |
| */ |
| @Override |
| 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); |
| } |
| |
| /** |
| * 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<>(); |
| 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(); |
| } |
| } |