Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultViewHandlerSupport |
|
| 0.0;0 | ||||
DefaultViewHandlerSupport$FacesServletMapping |
|
| 0.0;0 |
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.application; | |
20 | ||
21 | import org.apache.commons.logging.Log; | |
22 | import org.apache.commons.logging.LogFactory; | |
23 | ||
24 | import javax.faces.application.ViewHandler; | |
25 | import javax.faces.context.ExternalContext; | |
26 | import javax.faces.context.FacesContext; | |
27 | import java.util.Map; | |
28 | ||
29 | /** | |
30 | * A ViewHandlerSupport implementation for use with standard Java Servlet engines, | |
31 | * ie an engine that supports javax.servlet, and uses a standard web.xml file. | |
32 | * | |
33 | * @author Mathias Broekelmann (latest modification by $Author: skitching $) | |
34 | * @version $Revision: 673811 $ $Date: 2008-07-03 16:18:36 -0500 (Thu, 03 Jul 2008) $ | |
35 | */ | |
36 | 0 | public class DefaultViewHandlerSupport implements ViewHandlerSupport |
37 | { | |
38 | /** | |
39 | * Identifies the FacesServlet mapping in the current request map. | |
40 | */ | |
41 | 0 | private static final String CACHED_SERVLET_MAPPING = |
42 | DefaultViewHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING"; | |
43 | ||
44 | 0 | private static final Log log = LogFactory.getLog(DefaultViewHandlerSupport.class); |
45 | ||
46 | public String calculateViewId(FacesContext context, String viewId) | |
47 | { | |
48 | 0 | FacesServletMapping mapping = getFacesServletMapping(context); |
49 | 0 | if (mapping == null || mapping.isExtensionMapping()) |
50 | { | |
51 | 0 | viewId = applyDefaultSuffix(context, viewId); |
52 | } | |
53 | 0 | else if (mapping != null && viewId != null && mapping.getUrlPattern().startsWith(viewId)) |
54 | { | |
55 | 0 | throw new InvalidViewIdException(viewId); |
56 | } | |
57 | 0 | return viewId; |
58 | } | |
59 | ||
60 | public String calculateActionURL(FacesContext context, String viewId) | |
61 | { | |
62 | 0 | if (viewId == null || !viewId.startsWith("/")) |
63 | { | |
64 | 0 | throw new IllegalArgumentException("ViewId must start with a '/': " + viewId); |
65 | } | |
66 | ||
67 | 0 | FacesServletMapping mapping = getFacesServletMapping(context); |
68 | 0 | ExternalContext externalContext = context.getExternalContext(); |
69 | 0 | String contextPath = externalContext.getRequestContextPath(); |
70 | 0 | StringBuilder builder = new StringBuilder(contextPath); |
71 | 0 | if (mapping != null) |
72 | { | |
73 | 0 | if (mapping.isExtensionMapping()) |
74 | { | |
75 | 0 | String contextSuffix = getContextSuffix(context); |
76 | 0 | if (viewId.endsWith(contextSuffix)) |
77 | { | |
78 | 0 | builder.append(viewId.substring(0, viewId.indexOf(contextSuffix))); |
79 | 0 | builder.append(mapping.getExtension()); |
80 | } | |
81 | else | |
82 | { | |
83 | 0 | builder.append(viewId); |
84 | } | |
85 | 0 | } |
86 | else | |
87 | { | |
88 | 0 | builder.append(mapping.getPrefix()); |
89 | 0 | builder.append(viewId); |
90 | } | |
91 | } | |
92 | else | |
93 | { | |
94 | 0 | builder.append(viewId); |
95 | } | |
96 | 0 | String calculatedActionURL = builder.toString(); |
97 | 0 | if (log.isTraceEnabled()) |
98 | { | |
99 | 0 | log.trace("Calculated actionURL: '" + calculatedActionURL + "' for viewId: '" + viewId + "'"); |
100 | } | |
101 | 0 | return calculatedActionURL; |
102 | } | |
103 | ||
104 | /** | |
105 | * Read the web.xml file that is in the classpath and parse its internals to | |
106 | * figure out how the FacesServlet is mapped for the current webapp. | |
107 | */ | |
108 | protected FacesServletMapping getFacesServletMapping(FacesContext context) | |
109 | { | |
110 | 0 | Map<String, Object> requestMap = context.getExternalContext().getRequestMap(); |
111 | ||
112 | // Has the mapping already been determined during this request? | |
113 | 0 | if (!requestMap.containsKey(CACHED_SERVLET_MAPPING)) |
114 | { | |
115 | 0 | ExternalContext externalContext = context.getExternalContext(); |
116 | 0 | FacesServletMapping mapping = |
117 | calculateFacesServletMapping( | |
118 | externalContext.getRequestServletPath(), | |
119 | externalContext.getRequestPathInfo()); | |
120 | ||
121 | 0 | requestMap.put(CACHED_SERVLET_MAPPING, mapping); |
122 | } | |
123 | ||
124 | 0 | return (FacesServletMapping) requestMap.get(CACHED_SERVLET_MAPPING); |
125 | } | |
126 | ||
127 | /** | |
128 | * Determines the mapping of the FacesServlet in the web.xml configuration | |
129 | * file. However, there is no need to actually parse this configuration file | |
130 | * as runtime information is sufficient. | |
131 | * | |
132 | * @param servletPath The servletPath of the current request | |
133 | * @param pathInfo The pathInfo of the current request | |
134 | * @return the mapping of the FacesServlet in the web.xml configuration file | |
135 | */ | |
136 | protected static FacesServletMapping calculateFacesServletMapping( | |
137 | String servletPath, String pathInfo) | |
138 | { | |
139 | 0 | if (pathInfo != null) |
140 | { | |
141 | // If there is a "extra path", it's definitely no extension mapping. | |
142 | // Now we just have to determine the path which has been specified | |
143 | // in the url-pattern, but that's easy as it's the same as the | |
144 | // current servletPath. It doesn't even matter if "/*" has been used | |
145 | // as in this case the servletPath is just an empty string according | |
146 | // to the Servlet Specification (SRV 4.4). | |
147 | 0 | return FacesServletMapping.createPrefixMapping(servletPath); |
148 | } | |
149 | else | |
150 | { | |
151 | // In the case of extension mapping, no "extra path" is available. | |
152 | // Still it's possible that prefix-based mapping has been used. | |
153 | // Actually, if there was an exact match no "extra path" | |
154 | // is available (e.g. if the url-pattern is "/faces/*" | |
155 | // and the request-uri is "/context/faces"). | |
156 | 0 | int slashPos = servletPath.lastIndexOf('/'); |
157 | 0 | int extensionPos = servletPath.lastIndexOf('.'); |
158 | 0 | if (extensionPos > -1 && extensionPos > slashPos) |
159 | { | |
160 | 0 | String extension = servletPath.substring(extensionPos); |
161 | 0 | return FacesServletMapping.createExtensionMapping(extension); |
162 | } | |
163 | else | |
164 | { | |
165 | // There is no extension in the given servletPath and therefore | |
166 | // we assume that it's an exact match using prefix-based mapping. | |
167 | 0 | return FacesServletMapping.createPrefixMapping(servletPath); |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | protected String getContextSuffix(FacesContext context) | |
173 | { | |
174 | 0 | String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME); |
175 | 0 | if (defaultSuffix == null) |
176 | { | |
177 | 0 | defaultSuffix = ViewHandler.DEFAULT_SUFFIX; |
178 | } | |
179 | 0 | return defaultSuffix; |
180 | } | |
181 | ||
182 | /** | |
183 | * Return the viewId with any non-standard suffix stripped off and replaced with | |
184 | * the default suffix configured for the specified context. | |
185 | * <p/> | |
186 | * For example, an input parameter of "/foo.jsf" may return "/foo.jsp". | |
187 | */ | |
188 | protected String applyDefaultSuffix(FacesContext context, String viewId) | |
189 | { | |
190 | 0 | String defaultSuffix = getContextSuffix(context); |
191 | ||
192 | 0 | if (viewId == null) |
193 | { | |
194 | 0 | return null; |
195 | } | |
196 | ||
197 | 0 | if (!viewId.endsWith(defaultSuffix)) |
198 | { | |
199 | 0 | StringBuilder builder = new StringBuilder(viewId); |
200 | 0 | int slashPos = viewId.lastIndexOf('/'); |
201 | 0 | int extensionPos = viewId.lastIndexOf('.'); |
202 | 0 | if (extensionPos > -1 && extensionPos > slashPos) |
203 | { | |
204 | 0 | builder.replace(extensionPos, viewId.length(), defaultSuffix); |
205 | } | |
206 | else | |
207 | { | |
208 | 0 | builder.append(defaultSuffix); |
209 | } | |
210 | 0 | viewId = builder.toString(); |
211 | 0 | if (log.isTraceEnabled()) |
212 | { | |
213 | 0 | log.trace("view id after applying the context suffix: " + viewId); |
214 | } | |
215 | } | |
216 | 0 | return viewId; |
217 | } | |
218 | ||
219 | /** | |
220 | * Represents a mapping entry of the FacesServlet in the web.xml | |
221 | * configuration file. | |
222 | */ | |
223 | 0 | protected static class FacesServletMapping |
224 | { | |
225 | ||
226 | /** | |
227 | * The path ("/faces", for example) which has been specified in the | |
228 | * url-pattern of the FacesServlet mapping. | |
229 | */ | |
230 | private String prefix; | |
231 | ||
232 | /** | |
233 | * The extension (".jsf", for example) which has been specified in the | |
234 | * url-pattern of the FacesServlet mapping. | |
235 | */ | |
236 | private String extension; | |
237 | ||
238 | /** | |
239 | * Creates a new FacesServletMapping object using prefix mapping. | |
240 | * | |
241 | * @param path The path ("/faces", for example) which has been specified | |
242 | * in the url-pattern of the FacesServlet mapping. | |
243 | * @return a newly created FacesServletMapping | |
244 | */ | |
245 | public static FacesServletMapping createPrefixMapping(String path) | |
246 | { | |
247 | 0 | FacesServletMapping mapping = new FacesServletMapping(); |
248 | 0 | mapping.setPrefix(path); |
249 | 0 | return mapping; |
250 | } | |
251 | ||
252 | /** | |
253 | * Creates a new FacesServletMapping object using extension mapping. | |
254 | * | |
255 | * @param path The extension (".jsf", for example) which has been | |
256 | * specified in the url-pattern of the FacesServlet mapping. | |
257 | * @return a newly created FacesServletMapping | |
258 | */ | |
259 | public static FacesServletMapping createExtensionMapping( | |
260 | String extension) | |
261 | { | |
262 | 0 | FacesServletMapping mapping = new FacesServletMapping(); |
263 | 0 | mapping.setExtension(extension); |
264 | 0 | return mapping; |
265 | } | |
266 | ||
267 | /** | |
268 | * Returns the path ("/faces", for example) which has been specified in | |
269 | * the url-pattern of the FacesServlet mapping. If this mapping is based | |
270 | * on an extension, <code>null</code> will be returned. Note that this | |
271 | * path is not the same as the specified url-pattern as the trailing | |
272 | * "/*" is omitted. | |
273 | * | |
274 | * @return the path which has been specified in the url-pattern | |
275 | */ | |
276 | public String getPrefix() | |
277 | { | |
278 | 0 | return prefix; |
279 | } | |
280 | ||
281 | /** | |
282 | * Sets the path ("/faces/", for example) which has been specified in | |
283 | * the url-pattern. | |
284 | * | |
285 | * @param path The path which has been specified in the url-pattern | |
286 | */ | |
287 | public void setPrefix(String path) | |
288 | { | |
289 | 0 | this.prefix = path; |
290 | 0 | } |
291 | ||
292 | /** | |
293 | * Returns the extension (".jsf", for example) which has been specified | |
294 | * in the url-pattern of the FacesServlet mapping. If this mapping is | |
295 | * not based on an extension, <code>null</code> will be returned. | |
296 | * | |
297 | * @return the extension which has been specified in the url-pattern | |
298 | */ | |
299 | public String getExtension() | |
300 | { | |
301 | 0 | return extension; |
302 | } | |
303 | ||
304 | /** | |
305 | * Sets the extension (".jsf", for example) which has been specified in | |
306 | * the url-pattern of the FacesServlet mapping. | |
307 | * | |
308 | * @param extension The extension which has been specified in the url-pattern | |
309 | */ | |
310 | public void setExtension(String extension) | |
311 | { | |
312 | 0 | this.extension = extension; |
313 | 0 | } |
314 | ||
315 | /** | |
316 | * Indicates whether this mapping is based on an extension (e.g. | |
317 | * ".jsp"). | |
318 | * | |
319 | * @return <code>true</code>, if this mapping is based is on an | |
320 | * extension, <code>false</code> otherwise | |
321 | */ | |
322 | public boolean isExtensionMapping() | |
323 | { | |
324 | 0 | return extension != null; |
325 | } | |
326 | ||
327 | /** | |
328 | * Returns the url-pattern entry for this servlet mapping. | |
329 | * | |
330 | * @return the url-pattern entry for this servlet mapping | |
331 | */ | |
332 | public String getUrlPattern() | |
333 | { | |
334 | 0 | if (isExtensionMapping()) |
335 | { | |
336 | 0 | return "*" + extension; |
337 | } | |
338 | else | |
339 | { | |
340 | 0 | return prefix + "/*"; |
341 | } | |
342 | } | |
343 | ||
344 | } | |
345 | } |