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 javax.faces.context.ExternalContext;
24  import javax.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.util.WebConfigParamUtils;
29  
30  /**
31   * A ResourceHandlerSupport implementation for use with standard Java Servlet engines,
32   * ie an engine that supports javax.servlet, and uses a standard web.xml file.
33   * 
34   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
35   * @version $Revision: 1200044 $ $Date: 2011-11-09 18:44:02 -0500 (Wed, 09 Nov 2011) $
36   */
37  public class BaseResourceHandlerSupport extends ResourceHandlerSupport
38  {
39  
40      /**
41       * Set the max time in miliseconds set on the "Expires" header for a resource rendered by 
42       * the default ResourceHandler.
43       * (default to one week in miliseconds or 604800000) 
44       */
45      @JSFWebConfigParam(since="2.0", defaultValue="604800000", group="resources", tags="performance")
46      public static final String RESOURCE_MAX_TIME_EXPIRES = "org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES";
47  
48      /**
49       * Identifies the FacesServlet mapping in the current request map.
50       */
51      private static final String CACHED_SERVLET_MAPPING =
52          BaseResourceHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
53      
54      private Long _startupTime;
55      
56      private Long _maxTimeExpires;
57          
58      public BaseResourceHandlerSupport()
59      {
60          _startupTime = System.currentTimeMillis();
61      }
62      
63      public ResourceLoader[] getResourceLoaders()
64      {
65          return null;
66      }
67  
68      public String calculateResourceBasePath(FacesContext facesContext)
69      {        
70          FacesServletMapping mapping = getFacesServletMapping(facesContext);
71          ExternalContext externalContext = facesContext.getExternalContext();      
72          
73          if (mapping != null)
74          {
75              String resourceBasePath = null;
76              if (mapping.isExtensionMapping())
77              {
78                  // Mapping using a suffix. In this case we have to strip 
79                  // the suffix. If we have a url like:
80                  // http://localhost:8080/testjsf20/javax.faces.resource/imagen.jpg.jsf?ln=dojo
81                  // 
82                  // The servlet path is /javax.faces.resource/imagen.jpg.jsf
83                  //
84                  // For obtain the resource name we have to remove the .jsf suffix and 
85                  // the prefix ResourceHandler.RESOURCE_IDENTIFIER
86                  resourceBasePath = externalContext.getRequestServletPath();
87                  int stripPoint = resourceBasePath.lastIndexOf('.');
88                  if (stripPoint > 0)
89                  {
90                      resourceBasePath = resourceBasePath.substring(0, stripPoint);
91                  }
92              }
93              else
94              {
95                  // Mapping using prefix. In this case we have to strip 
96                  // the prefix used for mapping. If we have a url like:
97                  // http://localhost:8080/testjsf20/faces/javax.faces.resource/imagen.jpg?ln=dojo
98                  //
99                  // The servlet path is /faces
100                 // and the path info is /javax.faces.resource/imagen.jpg
101                 //
102                 // For obtain the resource name we have to remove the /faces prefix and 
103                 // then the prefix ResourceHandler.RESOURCE_IDENTIFIER
104                 resourceBasePath = externalContext.getRequestPathInfo();
105             }
106             return resourceBasePath;            
107         }
108         else
109         {
110             //If no mapping is detected, just return the
111             //information follows the servlet path but before
112             //the query string
113             return externalContext.getRequestPathInfo();
114         }
115     }
116 
117     public boolean isExtensionMapping()
118     {
119         FacesServletMapping mapping = getFacesServletMapping(
120                 FacesContext.getCurrentInstance());
121         if (mapping != null)
122         {
123             if (mapping.isExtensionMapping())
124             {
125                 return true;
126             }
127         }
128         return false;
129     }
130     
131     public String getMapping()
132     {
133         FacesServletMapping mapping = getFacesServletMapping(
134                 FacesContext.getCurrentInstance());
135         if (mapping != null)
136         {
137             if (mapping.isExtensionMapping())
138             {
139                 return mapping.getExtension();
140             }
141             else
142             {
143                 return mapping.getPrefix();
144             }
145         }
146         return "";
147     }
148 
149     /**
150      * Read the web.xml file that is in the classpath and parse its internals to
151      * figure out how the FacesServlet is mapped for the current webapp.
152      */
153     protected FacesServletMapping getFacesServletMapping(FacesContext context)
154     {
155         Map<Object, Object> attributes = context.getAttributes();
156 
157         // Has the mapping already been determined during this request?
158         FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
159         if (mapping == null)
160         {
161             ExternalContext externalContext = context.getExternalContext();
162             mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
163                     externalContext.getRequestPathInfo());
164 
165             attributes.put(CACHED_SERVLET_MAPPING, mapping);
166         }
167         return mapping;
168     }
169 
170     /**
171      * Determines the mapping of the FacesServlet in the web.xml configuration
172      * file. However, there is no need to actually parse this configuration file
173      * as runtime information is sufficient.
174      *
175      * @param servletPath The servletPath of the current request
176      * @param pathInfo    The pathInfo of the current request
177      * @return the mapping of the FacesServlet in the web.xml configuration file
178      */
179     protected static FacesServletMapping calculateFacesServletMapping(
180         String servletPath, String pathInfo)
181     {
182         if (pathInfo != null)
183         {
184             // If there is a "extra path", it's definitely no extension mapping.
185             // Now we just have to determine the path which has been specified
186             // in the url-pattern, but that's easy as it's the same as the
187             // current servletPath. It doesn't even matter if "/*" has been used
188             // as in this case the servletPath is just an empty string according
189             // to the Servlet Specification (SRV 4.4).
190             return FacesServletMapping.createPrefixMapping(servletPath);
191         }
192         else
193         {
194             // In the case of extension mapping, no "extra path" is available.
195             // Still it's possible that prefix-based mapping has been used.
196             // Actually, if there was an exact match no "extra path"
197             // is available (e.g. if the url-pattern is "/faces/*"
198             // and the request-uri is "/context/faces").
199             int slashPos = servletPath.lastIndexOf('/');
200             int extensionPos = servletPath.lastIndexOf('.');
201             if (extensionPos > -1 && extensionPos > slashPos)
202             {
203                 String extension = servletPath.substring(extensionPos);
204                 return FacesServletMapping.createExtensionMapping(extension);
205             }
206             else
207             {
208                 // There is no extension in the given servletPath and therefore
209                 // we assume that it's an exact match using prefix-based mapping.
210                 return FacesServletMapping.createPrefixMapping(servletPath);
211             }
212         }
213     }
214 
215     public long getStartupTime()
216     {
217         return _startupTime;
218     }
219     
220     public long getMaxTimeExpires()
221     {
222         if (_maxTimeExpires == null)
223         {
224             _maxTimeExpires = WebConfigParamUtils.getLongInitParameter(
225                     FacesContext.getCurrentInstance().getExternalContext(), 
226                     RESOURCE_MAX_TIME_EXPIRES, 604800000L);
227         }
228         return _maxTimeExpires;
229     }
230 }