View Javadoc

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 }