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