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 }