1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.application;
20
21 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
22 import org.apache.myfaces.shared.resource.ResourceHandlerCache;
23 import org.apache.myfaces.shared.resource.ResourceHandlerCache.ResourceValue;
24 import org.apache.myfaces.shared.resource.ResourceHandlerSupport;
25 import org.apache.myfaces.shared.resource.ResourceImpl;
26 import org.apache.myfaces.shared.resource.ResourceLoader;
27 import org.apache.myfaces.shared.resource.ResourceMeta;
28 import org.apache.myfaces.shared.resource.ResourceValidationUtils;
29 import org.apache.myfaces.shared.util.ClassUtils;
30 import org.apache.myfaces.shared.util.ExternalContextUtils;
31 import org.apache.myfaces.shared.util.StringUtils;
32 import org.apache.myfaces.shared.util.WebConfigParamUtils;
33
34 import javax.faces.application.Resource;
35 import javax.faces.application.ResourceHandler;
36 import javax.faces.application.ResourceWrapper;
37 import javax.faces.context.ExternalContext;
38 import javax.faces.context.FacesContext;
39 import javax.servlet.http.HttpServletResponse;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43 import java.net.URL;
44 import java.util.Locale;
45 import java.util.Map;
46 import java.util.MissingResourceException;
47 import java.util.ResourceBundle;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50
51
52
53
54
55
56
57
58 public class ResourceHandlerImpl extends ResourceHandler
59 {
60
61 private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
62
63 private ResourceHandlerSupport _resourceHandlerSupport;
64
65 private ResourceHandlerCache _resourceHandlerCache;
66
67
68 private static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName());
69
70
71
72
73 @JSFWebConfigParam(since="2.1.6, 2.0.12", defaultValue="false",
74 expectedValues="true, false", group="resources")
75 public static final String INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME =
76 "org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME";
77 public static final boolean INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT = false;
78
79
80
81
82
83
84 @JSFWebConfigParam(since="2.1.10, 2.0.16", defaultValue="2048", group="resources")
85 public static final String INIT_PARAM_RESOURCE_BUFFER_SIZE = "org.apache.myfaces.RESOURCE_BUFFER_SIZE";
86 public static final int INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT = 2048;
87
88 private Boolean _allowSlashLibraryName;
89 private int _resourceBufferSize = -1;
90
91 private String[] _excludedResourceExtensions;
92
93 @Override
94 public Resource createResource(String resourceName)
95 {
96 return createResource(resourceName, null);
97 }
98
99 @Override
100 public Resource createResource(String resourceName, String libraryName)
101 {
102 return createResource(resourceName, libraryName, null);
103 }
104
105 @Override
106 public Resource createResource(String resourceName, String libraryName,
107 String contentType)
108 {
109 Resource resource = null;
110
111 if (!ResourceValidationUtils.isValidResourceName(resourceName))
112 {
113 return null;
114 }
115 if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
116 libraryName, isAllowSlashesLibraryName()))
117 {
118 return null;
119 }
120
121 if (contentType == null)
122 {
123
124 contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(resourceName);
125 }
126
127 final String localePrefix = getLocalePrefixForLocateResource();
128
129
130 if(getResourceLoaderCache().containsResource(resourceName, libraryName, contentType, localePrefix))
131 {
132 ResourceValue resourceValue = getResourceLoaderCache().getResource(
133 resourceName, libraryName, contentType, localePrefix);
134
135 resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
136 getResourceHandlerSupport(), contentType);
137 }
138 else
139 {
140 for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
141 {
142 ResourceMeta resourceMeta = deriveResourceMeta(loader, resourceName, libraryName, localePrefix);
143
144 if (resourceMeta != null)
145 {
146 resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
147
148
149 getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
150 localePrefix, resourceMeta, loader);
151 break;
152 }
153 }
154 }
155
156 return resource;
157 }
158
159
160
161
162
163
164
165 protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
166 String resourceName, String libraryName, String localePrefix)
167 {
168 String resourceVersion = null;
169 String libraryVersion = null;
170 ResourceMeta resourceId = null;
171
172
173 if (localePrefix != null)
174 {
175 if (null != libraryName)
176 {
177 String pathToLib = localePrefix + '/' + libraryName;
178 libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
179
180 if (null != libraryVersion)
181 {
182 String pathToResource = localePrefix + '/'
183 + libraryName + '/' + libraryVersion + '/'
184 + resourceName;
185 resourceVersion = resourceLoader
186 .getResourceVersion(pathToResource);
187 }
188 else
189 {
190 String pathToResource = localePrefix + '/'
191 + libraryName + '/' + resourceName;
192 resourceVersion = resourceLoader
193 .getResourceVersion(pathToResource);
194 }
195
196 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
197 {
198 resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
199 libraryVersion, resourceName, resourceVersion);
200 }
201 }
202 else
203 {
204 resourceVersion = resourceLoader
205 .getResourceVersion(localePrefix + '/'+ resourceName);
206 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
207 {
208 resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
209 resourceName, resourceVersion);
210 }
211 }
212
213 if (resourceId != null)
214 {
215 URL url = resourceLoader.getResourceURL(resourceId);
216 if (url == null)
217 {
218 resourceId = null;
219 }
220 }
221 }
222
223
224 if (resourceId == null)
225 {
226 if (null != libraryName)
227 {
228 libraryVersion = resourceLoader.getLibraryVersion(libraryName);
229
230 if (null != libraryVersion)
231 {
232 String pathToResource = (libraryName + '/' + libraryVersion
233 + '/' + resourceName);
234 resourceVersion = resourceLoader
235 .getResourceVersion(pathToResource);
236 }
237 else
238 {
239 String pathToResource = (libraryName + '/'
240 + resourceName);
241 resourceVersion = resourceLoader
242 .getResourceVersion(pathToResource);
243 }
244
245 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
246 {
247 resourceId = resourceLoader.createResourceMeta(null, libraryName,
248 libraryVersion, resourceName, resourceVersion);
249 }
250 }
251 else
252 {
253 resourceVersion = resourceLoader
254 .getResourceVersion(resourceName);
255
256 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
257 {
258 resourceId = resourceLoader.createResourceMeta(null, null, null,
259 resourceName, resourceVersion);
260 }
261 }
262
263 if (resourceId != null)
264 {
265 URL url = resourceLoader.getResourceURL(resourceId);
266 if (url == null)
267 {
268 resourceId = null;
269 }
270 }
271 }
272
273 return resourceId;
274 }
275
276 @Override
277 public String getRendererTypeForResourceName(String resourceName)
278 {
279 if (resourceName.endsWith(".js"))
280 {
281 return "javax.faces.resource.Script";
282 }
283 else if (resourceName.endsWith(".css"))
284 {
285 return "javax.faces.resource.Stylesheet";
286 }
287 return null;
288 }
289
290
291
292
293
294
295
296 @Override
297 public void handleResourceRequest(FacesContext facesContext) throws IOException
298 {
299
300
301 String resourceBasePath = getResourceHandlerSupport()
302 .calculateResourceBasePath(facesContext);
303
304 if (resourceBasePath == null)
305 {
306
307
308
309
310 return;
311 }
312
313
314
315
316
317
318 ExternalContext extContext = facesContext.getExternalContext();
319 Object response = extContext.getResponse();
320 HttpServletResponse httpServletResponse = ExternalContextUtils.getHttpServletResponse(response);
321 if (httpServletResponse == null)
322 {
323 throw new IllegalStateException("Could not obtain an instance of HttpServletResponse.");
324 }
325
326 if (isResourceIdentifierExcluded(facesContext, resourceBasePath))
327 {
328 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
329 return;
330 }
331
332 String resourceName = null;
333 if (resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
334 {
335 resourceName = resourceBasePath
336 .substring(ResourceHandler.RESOURCE_IDENTIFIER.length() + 1);
337
338 if (resourceBasePath != null && !ResourceValidationUtils.isValidResourceName(resourceName))
339 {
340 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
341 return;
342 }
343 }
344 else
345 {
346
347 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
348 return;
349 }
350
351 String libraryName = facesContext.getExternalContext()
352 .getRequestParameterMap().get("ln");
353
354 if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
355 libraryName, isAllowSlashesLibraryName()))
356 {
357 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
358 return;
359 }
360
361 Resource resource = null;
362 if (libraryName != null)
363 {
364
365 resource = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName);
366 }
367 else
368 {
369 resource = facesContext.getApplication().getResourceHandler().createResource(resourceName);
370 }
371
372 if (resource == null)
373 {
374 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
375 return;
376 }
377
378 if (!resource.userAgentNeedsUpdate(facesContext))
379 {
380 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
381 return;
382 }
383
384 httpServletResponse.setContentType(_getContentType(resource, facesContext.getExternalContext()));
385
386 Map<String, String> headers = resource.getResponseHeaders();
387
388 for (Map.Entry<String, String> entry : headers.entrySet())
389 {
390 httpServletResponse.setHeader(entry.getKey(), entry.getValue());
391 }
392
393
394 extContext.setResponseBufferSize(this.getResourceBufferSize());
395
396
397 try
398 {
399 InputStream in = resource.getInputStream();
400 OutputStream out = httpServletResponse.getOutputStream();
401
402 byte[] buffer = new byte[this.getResourceBufferSize()];
403
404 try
405 {
406 int count = pipeBytes(in, out, buffer);
407
408 if (!httpServletResponse.isCommitted())
409 {
410 httpServletResponse.setContentLength(count);
411 }
412 }
413 finally
414 {
415 try
416 {
417 in.close();
418 }
419 finally
420 {
421 out.close();
422 }
423 }
424 }
425 catch (IOException e)
426 {
427
428 if (log.isLoggable(Level.SEVERE))
429 {
430 log.log(Level.SEVERE,"Error trying to load resource " + resourceName
431 + " with library " + libraryName + " :"
432 + e.getMessage(), e);
433 }
434 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
435 }
436
437
438
439
440
441
442
443
444 }
445
446
447
448
449
450 private static int pipeBytes(InputStream in, OutputStream out, byte[] buffer)
451 throws IOException
452 {
453 int count = 0;
454 int length;
455
456 while ((length = (in.read(buffer))) >= 0)
457 {
458 out.write(buffer, 0, length);
459 count += length;
460 }
461 return count;
462 }
463
464 @Override
465 public boolean isResourceRequest(FacesContext facesContext)
466 {
467
468
469
470 Boolean value = (Boolean) facesContext.getAttributes().get(IS_RESOURCE_REQUEST);
471
472 if (value == null)
473 {
474 String resourceBasePath = getResourceHandlerSupport()
475 .calculateResourceBasePath(facesContext);
476
477 value = resourceBasePath != null
478 && resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER);
479 facesContext.getAttributes().put(IS_RESOURCE_REQUEST, value);
480 }
481 return value;
482 }
483
484 protected String getLocalePrefixForLocateResource()
485 {
486 String localePrefix = null;
487 FacesContext context = FacesContext.getCurrentInstance();
488 boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
489
490 if (isResourceRequest)
491 {
492 localePrefix = context.getExternalContext().getRequestParameterMap().get("loc");
493
494 if (localePrefix != null)
495 {
496 if (!ResourceValidationUtils.isValidLocalePrefix(localePrefix))
497 {
498 return null;
499 }
500 return localePrefix;
501 }
502 }
503
504 String bundleName = context.getApplication().getMessageBundle();
505
506 if (null != bundleName)
507 {
508 Locale locale = null;
509
510 if (isResourceRequest || context.getViewRoot() == null)
511 {
512 locale = context.getApplication().getViewHandler()
513 .calculateLocale(context);
514 }
515 else
516 {
517 locale = context.getViewRoot().getLocale();
518 }
519
520 try
521 {
522 ResourceBundle bundle = ResourceBundle
523 .getBundle(bundleName, locale, ClassUtils.getContextClassLoader());
524
525 if (bundle != null)
526 {
527 localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
528 }
529 }
530 catch (MissingResourceException e)
531 {
532
533 }
534 }
535 return localePrefix;
536 }
537
538 protected boolean isResourceIdentifierExcluded(FacesContext context, String resourceIdentifier)
539 {
540 if (_excludedResourceExtensions == null)
541 {
542 String value = WebConfigParamUtils.getStringInitParameter(context.getExternalContext(),
543 RESOURCE_EXCLUDES_PARAM_NAME,
544 RESOURCE_EXCLUDES_DEFAULT_VALUE);
545
546 _excludedResourceExtensions = StringUtils.splitShortString(value, ' ');
547 }
548
549 for (int i = 0; i < _excludedResourceExtensions.length; i++)
550 {
551 if (resourceIdentifier.endsWith(_excludedResourceExtensions[i]))
552 {
553 return true;
554 }
555 }
556 return false;
557 }
558
559
560
561
562
563
564
565 @Override
566 public boolean libraryExists(String libraryName)
567 {
568 String localePrefix = getLocalePrefixForLocateResource();
569
570 String pathToLib = null;
571
572 if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
573 libraryName, isAllowSlashesLibraryName()))
574 {
575 return false;
576 }
577
578 if (localePrefix != null)
579 {
580
581 pathToLib = localePrefix + '/' + libraryName;
582
583 for (ResourceLoader loader : getResourceHandlerSupport()
584 .getResourceLoaders())
585 {
586 if (loader.libraryExists(pathToLib))
587 {
588 return true;
589 }
590 }
591 }
592
593
594 for (ResourceLoader loader : getResourceHandlerSupport()
595 .getResourceLoaders())
596 {
597 if (loader.libraryExists(libraryName))
598 {
599 return true;
600 }
601 }
602
603 return false;
604 }
605
606
607
608
609
610 public void setResourceHandlerSupport(
611 ResourceHandlerSupport resourceHandlerSupport)
612 {
613 _resourceHandlerSupport = resourceHandlerSupport;
614 }
615
616
617
618
619 protected ResourceHandlerSupport getResourceHandlerSupport()
620 {
621 if (_resourceHandlerSupport == null)
622 {
623 _resourceHandlerSupport = new DefaultResourceHandlerSupport();
624 }
625 return _resourceHandlerSupport;
626 }
627
628 private ResourceHandlerCache getResourceLoaderCache()
629 {
630 if (_resourceHandlerCache == null)
631 {
632 _resourceHandlerCache = new ResourceHandlerCache();
633 }
634 return _resourceHandlerCache;
635 }
636
637 private String _getContentType(Resource resource, ExternalContext externalContext)
638 {
639 String contentType = resource.getContentType();
640
641
642 if (contentType == null || contentType.length() == 0)
643 {
644 String resourceName = getWrappedResourceName(resource);
645
646 if (resourceName != null)
647 {
648 contentType = externalContext.getMimeType(resourceName);
649 }
650 }
651
652 return contentType;
653 }
654
655
656
657
658
659
660
661
662 private String getWrappedResourceName(Resource resource)
663 {
664 String resourceName = resource.getResourceName();
665 if (resourceName != null)
666 {
667 return resourceName;
668 }
669
670 if (resource instanceof ResourceWrapper)
671 {
672 return getWrappedResourceName(((ResourceWrapper) resource).getWrapped());
673 }
674
675 return null;
676 }
677
678 protected boolean isAllowSlashesLibraryName()
679 {
680 if (_allowSlashLibraryName == null)
681 {
682 _allowSlashLibraryName = WebConfigParamUtils.getBooleanInitParameter(
683 FacesContext.getCurrentInstance().getExternalContext(),
684 INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME,
685 INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT);
686 }
687 return _allowSlashLibraryName;
688 }
689
690 protected int getResourceBufferSize()
691 {
692 if (_resourceBufferSize == -1)
693 {
694 _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
695 FacesContext.getCurrentInstance().getExternalContext(),
696 INIT_PARAM_RESOURCE_BUFFER_SIZE,
697 INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
698 }
699 return _resourceBufferSize;
700 }
701
702 }