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