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