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