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