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