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 builder.append(contextPath);
202 if (mapping != null)
203 {
204 if (mapping.isExtensionMapping())
205 {
206
207 String[] contextSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context);
208 boolean founded = false;
209 for (String contextSuffix : contextSuffixes)
210 {
211 if (viewId.endsWith(contextSuffix))
212 {
213 builder.append(viewId.substring(0, viewId.indexOf(contextSuffix)));
214 builder.append(mapping.getExtension());
215 founded = true;
216 break;
217 }
218 }
219 if (!founded)
220 {
221
222
223
224
225
226
227
228
229
230
231
232 if (viewId.endsWith(mapping.getExtension()))
233 {
234 builder.append(viewId);
235 }
236 else if(viewId.lastIndexOf(".") != -1 )
237 {
238 builder.append(viewId.substring(0,viewId.lastIndexOf(".")));
239 builder.append(contextSuffixes[0]);
240 }
241 else
242 {
243 builder.append(viewId);
244 builder.append(contextSuffixes[0]);
245 }
246 }
247 }
248 else
249 {
250 builder.append(mapping.getPrefix());
251 builder.append(viewId);
252 }
253 }
254 else
255 {
256 builder.append(viewId);
257 }
258 String calculatedActionURL = builder.toString();
259 if (log.isLoggable(Level.FINEST))
260 {
261 log.finest("Calculated actionURL: '" + calculatedActionURL + "' for viewId: '" + viewId + "'");
262 }
263 return calculatedActionURL;
264 }
265
266
267
268
269
270 protected FacesServletMapping getFacesServletMapping(FacesContext context)
271 {
272 Map<Object, Object> attributes = context.getAttributes();
273
274
275 FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
276 if (mapping == null)
277 {
278 ExternalContext externalContext = context.getExternalContext();
279 mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
280 externalContext.getRequestPathInfo());
281
282 attributes.put(CACHED_SERVLET_MAPPING, mapping);
283 }
284 return mapping;
285 }
286
287
288
289
290
291
292
293
294
295
296 protected static FacesServletMapping calculateFacesServletMapping(
297 String servletPath, String pathInfo)
298 {
299 if (pathInfo != null)
300 {
301
302
303
304
305
306
307 return FacesServletMapping.createPrefixMapping(servletPath);
308 }
309 else
310 {
311
312
313
314
315
316 int slashPos = servletPath.lastIndexOf('/');
317 int extensionPos = servletPath.lastIndexOf('.');
318 if (extensionPos > -1 && extensionPos > slashPos)
319 {
320 String extension = servletPath.substring(extensionPos);
321 return FacesServletMapping.createExtensionMapping(extension);
322 }
323 else
324 {
325
326
327 return FacesServletMapping.createPrefixMapping(servletPath);
328 }
329 }
330 }
331
332 protected String[] getContextSuffix(FacesContext context)
333 {
334 String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
335 if (defaultSuffix == null)
336 {
337 defaultSuffix = ViewHandler.DEFAULT_SUFFIX;
338 }
339 return StringUtils.splitShortString(defaultSuffix, ' ');
340 }
341
342 protected String getFaceletsContextSuffix(FacesContext context)
343 {
344 String defaultSuffix = context.getExternalContext().getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
345 if (defaultSuffix == null)
346 {
347 defaultSuffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
348 }
349 return defaultSuffix;
350 }
351
352
353
354 protected String[] getFaceletsViewMappings(FacesContext context)
355 {
356 String faceletsViewMappings= context.getExternalContext().getInitParameter(
357 ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
358 if(faceletsViewMappings == null)
359 {
360 faceletsViewMappings= context.getExternalContext().getInitParameter("facelets.VIEW_MAPPINGS");
361 }
362
363 return faceletsViewMappings == null ? null : StringUtils.splitShortString(faceletsViewMappings, ';');
364 }
365
366
367
368
369
370
371
372
373 protected String handlePrefixMapping(String viewId, String prefix)
374 {
375
376
377
378
379
380
381
382
383 if ("".equals(prefix))
384 {
385
386
387
388 prefix = "//";
389 }
390 else
391 {
392
393
394
395
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 return uri;
422 }
423
424
425
426
427
428
429
430 protected String handleSuffixMapping(FacesContext context, String requestViewId)
431 {
432 String[] faceletsViewMappings = _initialized ? _faceletsViewMappings : getFaceletsViewMappings(context);
433 String[] jspDefaultSuffixes = _initialized ? _contextSuffixes : getContextSuffix(context);
434
435 int slashPos = requestViewId.lastIndexOf('/');
436 int extensionPos = requestViewId.lastIndexOf('.');
437
438 StringBuilder builder = SharedStringBuilder.get(context, VIEW_HANDLER_SUPPORT_SB);
439
440
441 for (String defaultSuffix : jspDefaultSuffixes)
442 {
443
444 builder.setLength(0);
445 builder.append(requestViewId);
446
447 if (extensionPos > -1 && extensionPos > slashPos)
448 {
449 builder.replace(extensionPos, requestViewId.length(), defaultSuffix);
450 }
451 else
452 {
453 builder.append(defaultSuffix);
454 }
455 String candidateViewId = builder.toString();
456
457 if( faceletsViewMappings != null && faceletsViewMappings.length > 0 )
458 {
459 for (String mapping : faceletsViewMappings)
460 {
461 if(mapping.startsWith("/"))
462 {
463 continue;
464 }
465 if(mapping.equals(candidateViewId))
466 {
467 return candidateViewId;
468 }
469 if(mapping.startsWith("."))
470 {
471 builder.setLength(0);
472 builder.append(candidateViewId);
473 builder.replace(candidateViewId.lastIndexOf('.'), candidateViewId.length(), mapping);
474 String tempViewId = builder.toString();
475 if(checkResourceExists(context,tempViewId))
476 {
477 return tempViewId;
478 }
479 }
480 }
481 }
482
483
484 if(checkResourceExists(context,candidateViewId))
485 {
486 return candidateViewId;
487 }
488
489 }
490
491
492 String faceletsDefaultSuffix = _initialized ? _faceletsContextSufix : this.getFaceletsContextSuffix(context);
493 if (faceletsDefaultSuffix != null)
494 {
495 for (String defaultSuffix : jspDefaultSuffixes)
496 {
497 if (faceletsDefaultSuffix.equals(defaultSuffix))
498 {
499 faceletsDefaultSuffix = null;
500 break;
501 }
502 }
503 }
504 if (faceletsDefaultSuffix != null)
505 {
506
507 builder.setLength(0);
508 builder.append(requestViewId);
509
510 if (extensionPos > -1 && extensionPos > slashPos)
511 {
512 builder.replace(extensionPos, requestViewId.length(), faceletsDefaultSuffix);
513 }
514 else
515 {
516 builder.append(faceletsDefaultSuffix);
517 }
518
519 String candidateViewId = builder.toString();
520 if(checkResourceExists(context,candidateViewId))
521 {
522 return candidateViewId;
523 }
524 }
525
526
527 if(checkResourceExists(context,requestViewId))
528 {
529 return requestViewId;
530 }
531
532
533 return null;
534 }
535
536 protected boolean checkResourceExists(FacesContext context, String viewId)
537 {
538 try
539 {
540 if (isCheckedViewIdCachingEnabled(context))
541 {
542 Boolean resourceExists = getCheckedViewIDMap(context).get(
543 viewId);
544 if (resourceExists == null)
545 {
546 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
547 .getViewDeclarationLanguage(context, viewId);
548 if (vdl != null)
549 {
550 resourceExists = vdl.viewExists(context, viewId);
551 }
552 else
553 {
554
555 resourceExists = context.getExternalContext().getResource(
556 viewId) != null;
557 }
558 getCheckedViewIDMap(context).put(viewId, resourceExists);
559 }
560 return resourceExists;
561 }
562 else
563 {
564 ViewDeclarationLanguage vdl = context.getApplication().getViewHandler()
565 .getViewDeclarationLanguage(context, viewId);
566 if (vdl != null)
567 {
568 if (vdl.viewExists(context, viewId))
569 {
570 return true;
571 }
572 }
573 else
574 {
575
576 if (context.getExternalContext().getResource(viewId) != null)
577 {
578 return true;
579 }
580 }
581 }
582 }
583 catch(MalformedURLException e)
584 {
585
586 }
587 return false;
588 }
589
590 private ConcurrentLRUCache<String, Boolean> getCheckedViewIDMap(FacesContext context)
591 {
592 if (_checkedViewIdMap == null)
593 {
594 int maxSize = getViewIDCacheMaxSize(context);
595 _checkedViewIdMap = new ConcurrentLRUCache<String, Boolean>((maxSize * 4 + 3) / 3, maxSize);
596 }
597 return _checkedViewIdMap;
598 }
599
600 private boolean isCheckedViewIdCachingEnabled(FacesContext context)
601 {
602 if (_checkedViewIdCacheEnabled == null)
603 {
604
605 if (context.isProjectStage(ProjectStage.Development))
606 {
607 _checkedViewIdCacheEnabled = Boolean.FALSE;
608 }
609 else
610 {
611
612 _checkedViewIdCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(context.getExternalContext(),
613 CHECKED_VIEWID_CACHE_ENABLED_ATTRIBUTE,
614 CHECKED_VIEWID_CACHE_ENABLED_DEFAULT);
615 }
616
617 if (log.isLoggable(Level.FINE))
618 {
619 log.log(Level.FINE, "MyFaces ViewID Caching Enabled="
620 + _checkedViewIdCacheEnabled);
621 }
622 }
623 return _checkedViewIdCacheEnabled;
624 }
625
626 private int getViewIDCacheMaxSize(FacesContext context)
627 {
628 ExternalContext externalContext = context.getExternalContext();
629
630 return WebConfigParamUtils.getIntegerInitParameter(externalContext,
631 CHECKED_VIEWID_CACHE_SIZE_ATTRIBUTE, CHECKED_VIEWID_CACHE_DEFAULT_SIZE);
632 }
633 }