1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.myfaces.shared.resource; 20 21 import java.util.Map; 22 23 import jakarta.faces.context.ExternalContext; 24 import jakarta.faces.context.FacesContext; 25 26 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; 27 import org.apache.myfaces.shared.application.FacesServletMapping; 28 import org.apache.myfaces.shared.application.FacesServletMappingUtils; 29 import org.apache.myfaces.shared.util.WebConfigParamUtils; 30 31 /** 32 * A ResourceHandlerSupport implementation for use with standard Java Servlet engines, 33 * ie an engine that supports javax.servlet, and uses a standard web.xml file. 34 */ 35 public class BaseResourceHandlerSupport extends ResourceHandlerSupport 36 { 37 38 /** 39 * Set the max time in miliseconds set on the "Expires" header for a resource rendered by 40 * the default ResourceHandler. 41 * (default to one week in miliseconds or 604800000) 42 */ 43 @JSFWebConfigParam(since="2.0", defaultValue="604800000", group="resources", tags="performance") 44 public static final String RESOURCE_MAX_TIME_EXPIRES = "org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES"; 45 46 /** 47 * Identifies the FacesServlet mapping in the current request map. 48 */ 49 private static final String CACHED_SERVLET_MAPPING = 50 BaseResourceHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING"; 51 52 private static final ResourceLoader[] EMPTY_RESOURCE_LOADERS = new ResourceLoader[]{}; 53 private static final ContractResourceLoader[] EMPTY_CONTRACT_RESOURCE_LOADERS = 54 new ContractResourceLoader[]{}; 55 56 private Long _startupTime; 57 58 private Long _maxTimeExpires; 59 60 public BaseResourceHandlerSupport() 61 { 62 _startupTime = System.currentTimeMillis(); 63 } 64 65 public ResourceLoader[] getResourceLoaders() 66 { 67 return EMPTY_RESOURCE_LOADERS; 68 } 69 70 public ContractResourceLoader[] getContractResourceLoaders() 71 { 72 return EMPTY_CONTRACT_RESOURCE_LOADERS; 73 } 74 75 public ResourceLoader[] getViewResourceLoaders() 76 { 77 return EMPTY_RESOURCE_LOADERS; 78 } 79 80 public String calculateResourceBasePath(FacesContext facesContext) 81 { 82 FacesServletMapping mapping = getFacesServletMapping(facesContext); 83 ExternalContext externalContext = facesContext.getExternalContext(); 84 85 if (mapping != null) 86 { 87 String resourceBasePath = null; 88 if (mapping.isExtensionMapping()) 89 { 90 // Mapping using a suffix. In this case we have to strip 91 // the suffix. If we have a url like: 92 // http://localhost:8080/testjsf20/jakarta.faces.resource/imagen.jpg.jsf?ln=dojo 93 // 94 // The servlet path is /jakarta.faces.resource/imagen.jpg.jsf 95 // 96 // For obtain the resource name we have to remove the .jsf suffix and 97 // the prefix ResourceHandler.RESOURCE_IDENTIFIER 98 resourceBasePath = externalContext.getRequestServletPath(); 99 int stripPoint = resourceBasePath.lastIndexOf('.'); 100 if (stripPoint > 0) 101 { 102 resourceBasePath = resourceBasePath.substring(0, stripPoint); 103 } 104 } 105 else 106 { 107 // Mapping using prefix. In this case we have to strip 108 // the prefix used for mapping. If we have a url like: 109 // http://localhost:8080/testjsf20/faces/jakarta.faces.resource/imagen.jpg?ln=dojo 110 // 111 // The servlet path is /faces 112 // and the path info is /jakarta.faces.resource/imagen.jpg 113 // 114 // For obtain the resource name we have to remove the /faces prefix and 115 // then the prefix ResourceHandler.RESOURCE_IDENTIFIER 116 resourceBasePath = externalContext.getRequestPathInfo(); 117 } 118 return resourceBasePath; 119 } 120 else 121 { 122 //If no mapping is detected, just return the 123 //information follows the servlet path but before 124 //the query string 125 return externalContext.getRequestPathInfo(); 126 } 127 } 128 129 public boolean isExtensionMapping() 130 { 131 FacesServletMapping mapping = getFacesServletMapping( 132 FacesContext.getCurrentInstance()); 133 if (mapping != null) 134 { 135 if (mapping.isExtensionMapping()) 136 { 137 return true; 138 } 139 } 140 return false; 141 } 142 143 public String getMapping() 144 { 145 FacesServletMapping mapping = getFacesServletMapping( 146 FacesContext.getCurrentInstance()); 147 if (mapping != null) 148 { 149 if (mapping.isExtensionMapping()) 150 { 151 return mapping.getExtension(); 152 } 153 else 154 { 155 return mapping.getPrefix(); 156 } 157 } 158 return ""; 159 } 160 161 /** 162 * Read the web.xml file that is in the classpath and parse its internals to 163 * figure out how the FacesServlet is mapped for the current webapp. 164 */ 165 protected FacesServletMapping getFacesServletMapping(FacesContext context) 166 { 167 Map<Object, Object> attributes = context.getAttributes(); 168 169 // Has the mapping already been determined during this request? 170 FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING); 171 if (mapping == null) 172 { 173 ExternalContext externalContext = context.getExternalContext(); 174 mapping = FacesServletMappingUtils.calculateFacesServletMapping( 175 context, externalContext.getRequestServletPath(), 176 externalContext.getRequestPathInfo(), 177 false); 178 179 attributes.put(CACHED_SERVLET_MAPPING, mapping); 180 } 181 return mapping; 182 } 183 184 /* 185 protected static FacesServletMapping calculateFacesServletMapping( 186 FacesContext facesContext, String servletPath, String pathInfo) 187 { 188 if (ExternalContextUtils.isPortlet(facesContext.getExternalContext())) 189 { 190 return calculateFacesServletMapping(servletPath, pathInfo); 191 } 192 else 193 { 194 Object context = facesContext.getExternalContext().getContext(); 195 if (context instanceof ServletContext) 196 { 197 if (pathInfo != null) 198 { 199 // If there is a "extra path", it's definitely no extension mapping. 200 // Now we just have to determine the path which has been specified 201 // in the url-pattern, but that's easy as it's the same as the 202 // current servletPath. It doesn't even matter if "/*" has been used 203 // as in this case the servletPath is just an empty string according 204 // to the Servlet Specification (SRV 4.4). 205 return createMappingFromServletRegistration(facesContext, 206 (ServletContext)context, servletPath, pathInfo); 207 } 208 else 209 { 210 // In the case of extension mapping, no "extra path" is available. 211 // Still it's possible that prefix-based mapping has been used. 212 // Actually, if there was an exact match no "extra path" 213 // is available (e.g. if the url-pattern is "/faces/*" 214 // and the request-uri is "/context/faces"). 215 int slashPos = servletPath.lastIndexOf('/'); 216 int extensionPos = servletPath.lastIndexOf('.'); 217 if (extensionPos > -1 && extensionPos > slashPos) 218 { 219 String extension = servletPath.substring(extensionPos); 220 return FacesServletMapping.createExtensionMapping(extension); 221 } 222 else 223 { 224 // There is no extension in the given servletPath and therefore 225 // we assume that it's an exact match using prefix-based mapping. 226 return createMappingFromServletRegistration(facesContext, 227 (ServletContext)context, servletPath, pathInfo); 228 } 229 } 230 } 231 else 232 { 233 return calculateFacesServletMapping(servletPath, pathInfo); 234 } 235 } 236 //return null; 237 } 238 239 private static FacesServletMapping createMappingFromServletRegistration(FacesContext facesContext, 240 ServletContext servletContext, String servletPath, String pathInfo) 241 { 242 try 243 { 244 Map<String, ? extends ServletRegistration> map = servletContext.getServletRegistrations(); 245 if (map != null) 246 { 247 for (Map.Entry<String, ? extends ServletRegistration> entry : map.entrySet()) 248 { 249 try 250 { 251 Class servletClass = org.apache.myfaces.shared.util.ClassUtils.simpleClassForName( 252 (String)entry.getValue().getClassName()); 253 if (FacesServlet.class.isAssignableFrom(servletClass) || 254 DelegatedFacesServlet.class.isAssignableFrom(servletClass) || 255 servletClass.getName().equals( 256 WebXml.getWebXml(facesContext.getExternalContext()).getDelegateFacesServlet())) 257 { 258 Collection<String> mappings = entry.getValue().getMappings(); 259 for (String mapping : mappings) 260 { 261 if (mapping.startsWith("*.")) 262 { 263 // extension mapping, use it. 264 return FacesServletMapping.createExtensionMapping(mapping.substring(1)); 265 } 266 else if (mapping.startsWith("/") && mapping.endsWith("/*")) 267 { 268 // prefix mapping, use it. 269 return FacesServletMapping.createPrefixMapping( 270 mapping.substring(0, mapping.length()-2)); 271 } 272 } 273 } 274 } 275 catch (Exception ex) 276 { 277 //No op 278 } 279 } 280 return FacesServletMapping.createPrefixMapping(servletPath); 281 } 282 else 283 { 284 return FacesServletMapping.createPrefixMapping(servletPath); 285 } 286 } 287 catch(Exception ex) 288 { 289 return FacesServletMapping.createPrefixMapping(servletPath); 290 } 291 } 292 */ 293 294 /** 295 * Determines the mapping of the FacesServlet in the web.xml configuration 296 * file. However, there is no need to actually parse this configuration file 297 * as runtime information is sufficient. 298 * 299 * @param servletPath The servletPath of the current request 300 * @param pathInfo The pathInfo of the current request 301 * @return the mapping of the FacesServlet in the web.xml configuration file 302 */ 303 @Deprecated 304 protected static FacesServletMapping calculateFacesServletMapping( 305 String servletPath, String pathInfo) 306 { 307 if (pathInfo != null) 308 { 309 // If there is a "extra path", it's definitely no extension mapping. 310 // Now we just have to determine the path which has been specified 311 // in the url-pattern, but that's easy as it's the same as the 312 // current servletPath. It doesn't even matter if "/*" has been used 313 // as in this case the servletPath is just an empty string according 314 // to the Servlet Specification (SRV 4.4). 315 return FacesServletMapping.createPrefixMapping(servletPath); 316 } 317 else 318 { 319 // In the case of extension mapping, no "extra path" is available. 320 // Still it's possible that prefix-based mapping has been used. 321 // Actually, if there was an exact match no "extra path" 322 // is available (e.g. if the url-pattern is "/faces/*" 323 // and the request-uri is "/context/faces"). 324 int slashPos = servletPath.lastIndexOf('/'); 325 int extensionPos = servletPath.lastIndexOf('.'); 326 if (extensionPos > -1 && extensionPos > slashPos) 327 { 328 String extension = servletPath.substring(extensionPos); 329 return FacesServletMapping.createExtensionMapping(extension); 330 } 331 else 332 { 333 // There is no extension in the given servletPath and therefore 334 // we assume that it's an exact match using prefix-based mapping. 335 return FacesServletMapping.createPrefixMapping(servletPath); 336 } 337 } 338 } 339 340 public long getStartupTime() 341 { 342 return _startupTime; 343 } 344 345 public long getMaxTimeExpires() 346 { 347 if (_maxTimeExpires == null) 348 { 349 _maxTimeExpires = WebConfigParamUtils.getLongInitParameter( 350 FacesContext.getCurrentInstance().getExternalContext(), 351 RESOURCE_MAX_TIME_EXPIRES, 604800000L); 352 } 353 return _maxTimeExpires; 354 } 355 }