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