/* | |
* 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 java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Map; | |
import javax.faces.context.ExternalContext; | |
import javax.faces.context.FacesContext; | |
import javax.faces.webapp.FacesServlet; | |
import javax.servlet.ServletContext; | |
import javax.servlet.ServletRegistration; | |
import org.apache.myfaces.config.MyfacesConfig; | |
import org.apache.myfaces.util.lang.ClassUtils; | |
import org.apache.myfaces.util.ExternalContextUtils; | |
import org.apache.myfaces.webapp.DelegatedFacesServlet; | |
public class FacesServletMappingUtils | |
{ | |
private static final String FACES_SERVLET_REGISTRATION = "org.apache.myfaces.FACES_SERVLET_REGISTRATION"; | |
private static final String SERVLET_REGISTRATIONS = "org.apache.myfaces.SERVLET_REGISTRATIONS"; | |
private static final String CURRENT_REQUEST_FACES_SERVLET = "org.apache.myfaces.CURRENT_FACES_SERVLET_MAPPING"; | |
/** | |
* Wrapper for better performance | |
*/ | |
public static class ServletRegistrationInfo | |
{ | |
private String className; | |
private String[] mappings; | |
private boolean facesServlet; | |
private ServletRegistration registration; | |
public ServletRegistrationInfo(ServletRegistration registration, boolean facesServlet) | |
{ | |
this.className = registration.getClassName(); | |
this.facesServlet = facesServlet; | |
this.registration = registration; | |
Collection<String> mappingsCollection = registration.getMappings(); | |
mappings = mappingsCollection.toArray(new String[mappingsCollection.size()]); | |
if (mappings == null) | |
{ | |
mappings = new String[]{ }; | |
} | |
} | |
public String getClassName() | |
{ | |
return className; | |
} | |
public String[] getMappings() | |
{ | |
return mappings; | |
} | |
public boolean isFacesServlet() | |
{ | |
return facesServlet; | |
} | |
public ServletRegistration getRegistration() | |
{ | |
return registration; | |
} | |
} | |
public static FacesServletMapping getCurrentRequestFacesServletMapping(FacesContext context) | |
{ | |
Map<Object, Object> attributes = context.getAttributes(); | |
// Has the mapping already been determined during this request? | |
FacesServletMapping mapping = (FacesServletMapping) attributes.get(CURRENT_REQUEST_FACES_SERVLET); | |
if (mapping == null) | |
{ | |
ExternalContext externalContext = context.getExternalContext(); | |
mapping = calculateFacesServletMapping( | |
context, | |
externalContext.getRequestServletPath(), | |
externalContext.getRequestPathInfo(), | |
true); | |
attributes.put(CURRENT_REQUEST_FACES_SERVLET, mapping); | |
} | |
return mapping; | |
} | |
public static List<ServletRegistrationInfo> getServletRegistrations(FacesContext facesContext, | |
ServletContext servletContext, boolean cache) | |
{ | |
Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap(); | |
List<ServletRegistrationInfo> infos = | |
(List<ServletRegistrationInfo>) applicationMap.get(SERVLET_REGISTRATIONS); | |
if (infos == null) | |
{ | |
infos = new ArrayList<>(); | |
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations(); | |
if (registrations != null) | |
{ | |
for (ServletRegistration servletRegistration : registrations.values()) | |
{ | |
ServletRegistrationInfo info = new ServletRegistrationInfo(servletRegistration, | |
isFacesServlet(facesContext, servletRegistration.getClassName())); | |
infos.add(info); | |
} | |
} | |
infos = Collections.unmodifiableList(infos); | |
if (cache) | |
{ | |
applicationMap.put(SERVLET_REGISTRATIONS, infos); | |
} | |
} | |
return infos; | |
} | |
public static ServletRegistrationInfo getFacesServletRegistration(FacesContext facesContext, | |
ServletContext servletContext, boolean cache) | |
{ | |
Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap(); | |
ServletRegistrationInfo facesServletRegistration = (ServletRegistrationInfo) | |
applicationMap.get(FACES_SERVLET_REGISTRATION); | |
if (facesServletRegistration == null) | |
{ | |
for (ServletRegistrationInfo info : getServletRegistrations(facesContext, servletContext, cache)) | |
{ | |
if (info.isFacesServlet()) | |
{ | |
facesServletRegistration = info; | |
break; | |
} | |
} | |
if (facesServletRegistration != null && cache) | |
{ | |
applicationMap.put(FACES_SERVLET_REGISTRATION, facesServletRegistration); | |
} | |
} | |
return facesServletRegistration; | |
} | |
public static boolean isFacesServlet(FacesContext facesContext, String servletClassName) | |
{ | |
// shortcut to avoid class lookup | |
if (FacesServlet.class.getName().equals(servletClassName)) | |
{ | |
return true; | |
} | |
Class servletClass = ClassUtils.simpleClassForName(servletClassName, false); | |
if (servletClass != null) | |
{ | |
MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext); | |
return FacesServlet.class.isAssignableFrom(servletClass) | |
|| DelegatedFacesServlet.class.isAssignableFrom(servletClass) | |
|| servletClass.getName().equals(config.getDelegateFacesServlet()); | |
} | |
return false; | |
} | |
public static FacesServletMapping calculateFacesServletMapping( | |
FacesContext facesContext, String servletPath, String pathInfo, boolean allowExactMapping) | |
{ | |
if (ExternalContextUtils.isPortlet(facesContext.getExternalContext())) | |
{ | |
return calculateFacesServletMapping(servletPath, pathInfo); | |
} | |
else | |
{ | |
Object context = facesContext.getExternalContext().getContext(); | |
if (context instanceof ServletContext) | |
{ | |
if (pathInfo != null) | |
{ | |
// If there is a "extra path", it's definitely no extension mapping. | |
// Now we just have to determine the path which has been specified | |
// in the url-pattern, but that's easy as it's the same as the | |
// current servletPath. It doesn't even matter if "/*" has been used | |
// as in this case the servletPath is just an empty string according | |
// to the Servlet Specification (SRV 4.4). | |
return createMappingFromServletRegistration(facesContext, | |
(ServletContext)context, servletPath, pathInfo, allowExactMapping); | |
} | |
else | |
{ | |
// In the case of extension mapping, no "extra path" is available. | |
// Still it's possible that prefix-based mapping has been used. | |
// Actually, if there was an exact match no "extra path" | |
// is available (e.g. if the url-pattern is "/faces/*" | |
// and the request-uri is "/context/faces"). | |
String extension = extractExtensionFromUrl(servletPath); | |
if (extension != null) | |
{ | |
return FacesServletMapping.createExtensionMapping(extension); | |
} | |
else | |
{ | |
// There is no extension in the given servletPath and therefore | |
// we assume that it's an exact match using prefix-based mapping. | |
return createMappingFromServletRegistration(facesContext, | |
(ServletContext)context, servletPath, pathInfo, allowExactMapping); | |
} | |
} | |
} | |
else | |
{ | |
return calculateFacesServletMapping(servletPath, pathInfo); | |
} | |
} | |
} | |
private static FacesServletMapping createMappingFromServletRegistration(FacesContext facesContext, | |
ServletContext servletContext, String servletPath, String pathInfo, boolean allowExactMatch) | |
{ | |
try | |
{ | |
List<ServletRegistrationInfo> servletRegistrations = | |
getServletRegistrations(facesContext, servletContext, true); | |
if (servletRegistrations != null) | |
{ | |
FacesServletMapping facesExtensionMapping = null; | |
FacesServletMapping facesPrefixMapping = null; | |
FacesServletMapping facesExactMapping = null; | |
for (ServletRegistrationInfo servletRegistration : servletRegistrations) | |
{ | |
try | |
{ | |
if (servletRegistration.isFacesServlet()) | |
{ | |
for (String mapping : servletRegistration.getMappings()) | |
{ | |
if (isExtensionMapping(mapping)) | |
{ | |
facesExtensionMapping = FacesServletMapping.createExtensionMapping( | |
extractExtension(mapping)); | |
} | |
else if (isPrefixMapping(mapping)) | |
{ | |
facesPrefixMapping = FacesServletMapping.createPrefixMapping( | |
extractPrefix(mapping)); | |
} | |
else if (allowExactMatch && mapping.startsWith("/") && mapping.equals(servletPath)) | |
{ | |
facesExactMapping = FacesServletMapping.createExactMapping(servletPath); | |
} | |
} | |
} | |
else | |
{ | |
//This is not a FacesServlet mapping. | |
//It could be a non-faces request, we need to look for exact mapping to servletPath | |
//this happens with richfaces resources | |
for (String mapping : servletRegistration.getMappings()) | |
{ | |
if (mapping.startsWith("/") && mapping.endsWith("/*")) | |
{ | |
mapping = mapping.substring(0, mapping.length()-2); | |
} | |
if (mapping.equals(servletPath)) | |
{ | |
return FacesServletMapping.createPrefixMapping(mapping); | |
} | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
//No op | |
} | |
} | |
// Choose exact mapping if preferred. | |
if (allowExactMatch && facesExactMapping != null) | |
{ | |
return facesExactMapping; | |
} | |
else if (facesPrefixMapping != null) | |
{ | |
return facesPrefixMapping; | |
} | |
else if (facesExtensionMapping != null) | |
{ | |
return facesExtensionMapping; | |
} | |
else | |
{ | |
return FacesServletMapping.createPrefixMapping(servletPath); | |
} | |
} | |
else | |
{ | |
return FacesServletMapping.createPrefixMapping(servletPath); | |
} | |
} | |
catch(Exception ex) | |
{ | |
return FacesServletMapping.createPrefixMapping(servletPath); | |
} | |
} | |
/** | |
* Determines the mapping of the FacesServlet in the web.xml configuration | |
* file. However, there is no need to actually parse this configuration file | |
* as runtime information is sufficient. | |
* | |
* @param servletPath The servletPath of the current request | |
* @param pathInfo The pathInfo of the current request | |
* @return the mapping of the FacesServlet in the web.xml configuration file | |
*/ | |
private static FacesServletMapping calculateFacesServletMapping(String servletPath, String pathInfo) | |
{ | |
if (pathInfo != null) | |
{ | |
// If there is a "extra path", it's definitely no extension mapping. | |
// Now we just have to determine the path which has been specified | |
// in the url-pattern, but that's easy as it's the same as the | |
// current servletPath. It doesn't even matter if "/*" has been used | |
// as in this case the servletPath is just an empty string according | |
// to the Servlet Specification (SRV 4.4). | |
return FacesServletMapping.createPrefixMapping(servletPath); | |
} | |
else | |
{ | |
// In the case of extension mapping, no "extra path" is available. | |
// Still it's possible that prefix-based mapping has been used. | |
// Actually, if there was an exact match no "extra path" | |
// is available (e.g. if the url-pattern is "/faces/*" | |
// and the request-uri is "/context/faces"). | |
String extension = extractExtensionFromUrl(servletPath); | |
if (extension != null) | |
{ | |
return FacesServletMapping.createExtensionMapping(extension); | |
} | |
else | |
{ | |
// There is no extension in the given servletPath and therefore | |
// we assume that it's an exact match using prefix-based mapping. | |
return FacesServletMapping.createExactMapping(servletPath); | |
} | |
} | |
} | |
public static FacesServletMapping getExactMapping(FacesContext facesContext, String prefixedExactMappingViewId) | |
{ | |
if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext())) | |
{ | |
Object context = facesContext.getExternalContext().getContext(); | |
if (context instanceof ServletContext) | |
{ | |
ServletRegistrationInfo facesServletRegistration = | |
getFacesServletRegistration(facesContext, (ServletContext) context, true); | |
if (facesServletRegistration != null) | |
{ | |
for (String mapping : facesServletRegistration.getMappings()) | |
{ | |
if (!mapping.contains("*") && prefixedExactMappingViewId.equals(mapping)) | |
{ | |
return FacesServletMapping.createExactMapping(prefixedExactMappingViewId); | |
} | |
} | |
} | |
} | |
} | |
return null; | |
} | |
public static FacesServletMapping getGenericPrefixOrSuffixMapping(FacesContext facesContext) | |
{ | |
if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext())) | |
{ | |
Object context = facesContext.getExternalContext().getContext(); | |
if (context instanceof ServletContext) | |
{ | |
ServletRegistrationInfo facesServletRegistration = | |
getFacesServletRegistration(facesContext, (ServletContext) context, true); | |
if (facesServletRegistration != null) | |
{ | |
for (String mapping : facesServletRegistration.getMappings()) | |
{ | |
if (isExtensionMapping(mapping)) | |
{ | |
String extension = extractExtension(mapping); | |
return FacesServletMapping.createExtensionMapping(extension); | |
} | |
else if (isPrefixMapping(mapping)) | |
{ | |
String prefix = extractPrefix(mapping); | |
return FacesServletMapping.createPrefixMapping(prefix); | |
} | |
} | |
} | |
} | |
} | |
return null; | |
} | |
private static String extractExtensionFromUrl(String url) | |
{ | |
int slashPos = url.lastIndexOf('/'); | |
int extensionPos = url.lastIndexOf('.'); | |
if (extensionPos > -1 && extensionPos > slashPos) | |
{ | |
return url.substring(extensionPos); | |
} | |
return null; | |
} | |
private static boolean isExtensionMapping(String mapping) | |
{ | |
return mapping.startsWith("*."); | |
} | |
private static String extractExtension(String mapping) | |
{ | |
return mapping.substring(1); | |
} | |
private static boolean isPrefixMapping(String mapping) | |
{ | |
return mapping.startsWith("/") && mapping.endsWith("/*"); | |
} | |
private static String extractPrefix(String mapping) | |
{ | |
return mapping.substring(0, mapping.length() - 2); | |
} | |
} |