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