1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.shared.application;
20
21 import java.net.MalformedURLException;
22 import java.util.Map;
23 import java.util.logging.Level;
24 import java.util.logging.Logger;
25
26 import javax.faces.application.ProjectStage;
27 import javax.faces.application.ViewHandler;
28 import javax.faces.context.ExternalContext;
29 import javax.faces.context.FacesContext;
30
31 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
32 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
33 import org.apache.myfaces.shared.util.ConcurrentLRUCache;
34 import org.apache.myfaces.shared.util.ExternalContextUtils;
35 import org.apache.myfaces.shared.util.StringUtils;
36 import org.apache.myfaces.shared.util.WebConfigParamUtils;
37
38
39
40
41
42
43
44
45 public class DefaultViewHandlerSupport implements ViewHandlerSupport
46 {
47
48
49
50 private static final String CACHED_SERVLET_MAPPING =
51 DefaultViewHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
52
53
54 private static final Logger log = Logger.getLogger(DefaultViewHandlerSupport.class.getName());
55
56
57
58
59 @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="viewhandler", tags="performance",
60 classType="java.lang.Integer",
61 desc="Controls the size of the cache used to 'remember' if a view exists or not.")
62 private static final String CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.CHECKED_VIEWID_CACHE_SIZE";
63 private static final int CHECKED_VIEWID_CACHE_DEFAULT_SIZE = 500;
64
65
66
67
68
69 @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", expectedValues="true, false", group="viewhandler",
70 tags="performance",
71 desc="Enable or disable a cache used to 'remember' if a view exists or not and reduce the impact " +
72 "of sucesive calls to ExternalContext.getResource().")
73 private static final String CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE =
74 "org.apache.myfaces.CHECKED_VIEWID_CACHE_ENABLED";
75 private static final boolean CHECKED_VIEWID_CACHE_ENABLED_DEFAULT = true;
76
77 private static final String VIEW_HANDLER_SUPPORT_SB = "oam.viewhandler.SUPPORT_SB";
78
79 private volatile ConcurrentLRUCache<String, Boolean> _checkedViewIdMap = null;
80 private Boolean _checkedViewIdCacheEnabled = null;
81
82 public String calculateViewId(FacesContext context, String viewId)
83 {
84
85 if (viewId == null)
86 {
87 return null;
88 }
89 FacesServletMapping mapping = getFacesServletMapping(context);
90 if (mapping == null || mapping.isExtensionMapping())
91 {
92 viewId = handleSuffixMapping(context, viewId);
93 }
94 else if(mapping.isPrefixMapping())
95 {
96 viewId = handlePrefixMapping(viewId,mapping.getPrefix());
97
98
99
100
101
102 if (viewId != null && viewId.equals(mapping.getPrefix()) &&
103 !ExternalContextUtils.isPortlet(context.getExternalContext()))
104 {
105 throw new InvalidViewIdException();
106 }
107 }
108 else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
109 {
110 throw new InvalidViewIdException(viewId);
111 }
112
113
114
115
116
117
118 return viewId;
119 }
120
121 public String calculateAndCheckViewId(FacesContext context, String viewId)
122 {
123
124 if (viewId == null)
125 {
126 return null;
127 }
128 FacesServletMapping mapping = getFacesServletMapping(context);
129 if (mapping == null || mapping.isExtensionMapping())
130 {
131 viewId = handleSuffixMapping(context, viewId);
132 }
133 else if(mapping.isPrefixMapping())
134 {
135 viewId = handlePrefixMapping(viewId,mapping.getPrefix());
136
137 if(viewId != null)
138 {
139
140
141
142
143 if (viewId != null && viewId.equals(mapping.getPrefix()) &&
144 !ExternalContextUtils.isPortlet(context.getExternalContext()))
145 {
146 throw new InvalidViewIdException();
147 }
148
149 return (checkResourceExists(context,viewId) ? viewId : null);
150 }
151 }
152 else if (viewId != null && mapping.getUrlPattern().startsWith(viewId))
153 {
154 throw new InvalidViewIdException(viewId);
155 }
156 else
157 {
158 if(viewId != null)
159 {
160 return (checkResourceExists(context,viewId) ? viewId : null);
161 }
162 }
163
164 return viewId;
165 }
166
167 public String calculateActionURL(FacesContext context, String viewId)
168 {
169 if (viewId == null || !viewId.startsWith("/"))
170 {
171 throw new IllegalArgumentException("ViewId must start with a '/': " + viewId);
172 }
173
174 FacesServletMapping mapping = getFacesServletMapping(context);
175 ExternalContext externalContext = context.getExternalContext();
176 String contextPath = externalContext.getRequestContextPath();
177
178 StringBuilder builder = SharedStringBuilder.get(context, VIEW_HANDLER_SUPPORT_SB);
179 builder.append(contextPath);
180 if (mapping != null)
181 {
182 if (mapping.isExtensionMapping())
183 {
184
185 String[] contextSuffixes = getContextSuffix(context);
186 boolean founded = false;
187 for (String contextSuffix : contextSuffixes)
188 {
189 if (viewId.endsWith(contextSuffix))
190 {
191 builder.append(viewId.substring(0, viewId.indexOf(contextSuffix)));
192 builder.append(mapping.getExtension());
193 founded = true;
194 break;
195 }
196 }
197 if (!founded)
198 {
199
200
201
202
203
204
205
206
207
208
209
210 if (viewId.endsWith(mapping.getExtension()))
211 {
212 builder.append(viewId);
213 }
214 else if(viewId.lastIndexOf(".") != -1 )
215 {
216 builder.append(viewId.substring(0,viewId.lastIndexOf(".")));
217 builder.append(contextSuffixes[0]);
218 }
219 else
220 {
221 builder.append(viewId);
222 builder.append(contextSuffixes[0]);
223 }
224 }
225 }
226 else
227 {
228 builder.append(mapping.getPrefix());
229 builder.append(viewId);
230 }
231 }
232 else
233 {
234 builder.append(viewId);
235 }
236 String calculatedActionURL = builder.toString();
237 if (log.isLoggable(Level.FINEST))
238 {
239 log.finest("Calculated actionURL: '" + calculatedActionURL + "' for viewId: '" + viewId + "'");
240 }
241 return calculatedActionURL;
242 }
243
244
245
246
247
248 protected FacesServletMapping getFacesServletMapping(FacesContext context)
249 {
250 Map<Object, Object> attributes = context.getAttributes();
251
252
253 FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
254 if (mapping == null)
255 {
256 ExternalContext externalContext = context.getExternalContext();
257 mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
258 externalContext.getRequestPathInfo());
259
260 attributes.put(CACHED_SERVLET_MAPPING, mapping);
261 }
262 return mapping;
263 }
264
265
266
267
268
269
270
271
272
273
274 protected static FacesServletMapping calculateFacesServletMapping(
275 String servletPath, String pathInfo)
276 {
277 if (pathInfo != null)
278 {
279
280
281
282
283
284
285 return FacesServletMapping.createPrefixMapping(servletPath);
286 }
287 else
288 {
289
290
291
292
293
294 int slashPos = servletPath.lastIndexOf('/');
295 int extensionPos = servletPath.lastIndexOf('.');
296 if (extensionPos > -1 && extensionPos > slashPos)
297 {
298 String extension = servletPath.substring(extensionPos);
299 return FacesServletMapping.createExtensionMapping(extension);
300 }
301 else
302 {
303
304
305 return FacesServletMapping.createPrefixMapping(servletPath);
306 }
307 }
308 }
309
310 protected String[] getContextSuffix(FacesContext context)
311 {
312 String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
313 if (defaultSuffix == null)
314 {
315 defaultSuffix = ViewHandler.DEFAULT_SUFFIX;
316 }
317 return StringUtils.splitShortString(defaultSuffix, ' ');
318 }
319
320 protected String getFaceletsContextSuffix(FacesContext context)
321 {
322 String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
323 if (defaultSuffix == null)
324 {
325 defaultSuffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
326 }
327 return defaultSuffix;
328 }
329
330
331
332 protected String[] getFaceletsViewMappings(FacesContext context)
333 {
334 String faceletsViewMappings= context.getExternalContext().getInitParameter(
335 ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
336 if(faceletsViewMappings == null)
337 {
338 faceletsViewMappings= context.getExternalContext().getInitParameter("facelets.VIEW_MAPPINGS");
339 }
340
341 return faceletsViewMappings == null ? null : StringUtils.splitShortString(faceletsViewMappings, ';');
342 }
343
344
345
346
347
348
349
350
351 protected String handlePrefixMapping(String viewId, String prefix)
352 {
353
354
355
356
357
358
359
360
361 if ("".equals(prefix))
362 {
363
364
365
366 prefix = "//";
367 }
368 else
369 {
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 return uri;
400 }
401
402
403
404
405
406
407
408 protected String handleSuffixMapping(FacesContext context, String requestViewId)
409 {
410 String[] faceletsViewMappings = getFaceletsViewMappings(context);
411 String[] jspDefaultSuffixes = getContextSuffix(context);
412
413 int slashPos = requestViewId.lastIndexOf('/');
414 int extensionPos = requestViewId.lastIndexOf('.');
415
416 StringBuilder builder = SharedStringBuilder.get(context, VIEW_HANDLER_SUPPORT_SB);
417
418
419 for (String defaultSuffix : jspDefaultSuffixes)
420 {
421
422 builder.setLength(0);
423 builder.append(requestViewId);
424
425 if (extensionPos > -1 && extensionPos > slashPos)
426 {
427 builder.replace(extensionPos, requestViewId.length(), defaultSuffix);
428 }
429 else
430 {
431 builder.append(defaultSuffix);
432 }
433 String candidateViewId = builder.toString();
434
435 if( faceletsViewMappings != null && faceletsViewMappings.length > 0 )
436 {
437 for (String mapping : faceletsViewMappings)
438 {
439 if(mapping.startsWith("/"))
440 {
441 continue;
442 }
443 if(mapping.equals(candidateViewId))
444 {
445 return candidateViewId;
446 }
447 if(mapping.startsWith("."))
448 {
449 builder.setLength(0);
450 builder.append(candidateViewId);
451 builder.replace(candidateViewId.lastIndexOf('.'), candidateViewId.length(), mapping);
452 String tempViewId = builder.toString();
453 if(checkResourceExists(context,tempViewId))
454 {
455 return tempViewId;
456 }
457 }
458 }
459 }
460
461
462 if(checkResourceExists(context,candidateViewId))
463 {
464 return candidateViewId;
465 }
466
467 }
468
469
470 String faceletsDefaultSuffix = this.getFaceletsContextSuffix(context);
471 if (faceletsDefaultSuffix != null)
472 {
473 for (String defaultSuffix : jspDefaultSuffixes)
474 {
475 if (faceletsDefaultSuffix.equals(defaultSuffix))
476 {
477 faceletsDefaultSuffix = null;
478 break;
479 }
480 }
481 }
482 if (faceletsDefaultSuffix != null)
483 {
484
485 builder.setLength(0);
486 builder.append(requestViewId);
487
488 if (extensionPos > -1 && extensionPos > slashPos)
489 {
490 builder.replace(extensionPos, requestViewId.length(), faceletsDefaultSuffix);
491 }
492 else
493 {
494 builder.append(faceletsDefaultSuffix);
495 }
496
497 String candidateViewId = builder.toString();
498 if(checkResourceExists(context,candidateViewId))
499 {
500 return candidateViewId;
501 }
502 }
503
504
505 if(checkResourceExists(context,requestViewId))
506 {
507 return requestViewId;
508 }
509
510
511 return null;
512 }
513
514 protected boolean checkResourceExists(FacesContext context, String viewId)
515 {
516 try
517 {
518 if (isCheckedViewIdCachingEnabled(context))
519 {
520 Boolean resourceExists = getCheckedViewIDMap(context).get(
521 viewId);
522 if (resourceExists == null)
523 {
524 resourceExists = context.getExternalContext().getResource(
525 viewId) != null;
526 getCheckedViewIDMap(context).put(viewId, resourceExists);
527 }
528 return resourceExists;
529 }
530
531 if (context.getExternalContext().getResource(viewId) != null)
532 {
533 return true;
534 }
535 }
536 catch(MalformedURLException e)
537 {
538
539 }
540 return false;
541 }
542
543 private ConcurrentLRUCache<String, Boolean> getCheckedViewIDMap(FacesContext context)
544 {
545 if (_checkedViewIdMap == null)
546 {
547 int maxSize = getViewIDCacheMaxSize(context);
548 _checkedViewIdMap = new ConcurrentLRUCache<String, Boolean>((maxSize * 4 + 3) / 3, maxSize);
549 }
550 return _checkedViewIdMap;
551 }
552
553 private boolean isCheckedViewIdCachingEnabled(FacesContext context)
554 {
555 if (_checkedViewIdCacheEnabled == null)
556 {
557
558 if (context.isProjectStage(ProjectStage.Development))
559 {
560 _checkedViewIdCacheEnabled = Boolean.FALSE;
561 }
562 else
563 {
564
565 _checkedViewIdCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(),
566 CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
567 CHECKED_VIEWID_CACHE_ENABLED_DEFAULT);
568 }
569
570 if (log.isLoggable(Level.FINE))
571 {
572 log.log(Level.FINE, "MyFaces ViewID Caching Enabled="
573 + _checkedViewIdCacheEnabled);
574 }
575 }
576 return _checkedViewIdCacheEnabled;
577 }
578
579 private int getViewIDCacheMaxSize(FacesContext context)
580 {
581 ExternalContext externalContext = context.getExternalContext();
582
583 return WebConfigParamUtils.getIntegerInitParameter(externalContext,
584 CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE, CHECKED_VIEWID_CACHE_DEFAULT_SIZE);
585 }
586 }