1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.commons.resourcehandler.resource;
20
21 import java.io.InputStream;
22 import java.net.URL;
23
24 import javax.faces.application.ProjectStage;
25 import javax.faces.context.FacesContext;
26
27 import org.apache.myfaces.commons.util.ClassUtils;
28
29 /**
30 * A resource loader implementation which loads resources from the thread ClassLoader.
31 *
32 */
33 public class ClassLoaderResourceLoader extends ResourceLoader
34 {
35 //public final static String JAVAX_FACES_LIBRARY_NAME = "javax.faces";
36 //public final static String JSF_JS_RESOURCE_NAME = "jsf.js";
37
38 //public final static String MYFACES_JS_RESOURCE_NAME = "oamSubmit.js";
39 //public final static String MYFACES_LIBRARY_NAME = "org.apache.myfaces";
40
41
42 /**
43 * It checks version like this: 1, 1_0, 1_0_0, 100_100
44 *
45 * Used on getLibraryVersion to filter resource directories
46 **/
47 //protected static Pattern VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*");
48
49 /**
50 * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
51 *
52 * Used on getResourceVersion to filter resources
53 **/
54 //protected static Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
55
56 /*
57 private FileFilter _libraryFileFilter = new FileFilter()
58 {
59 public boolean accept(File pathname)
60 {
61 if (pathname.isDirectory() && VERSION_CHECKER.matcher(pathname.getName()).matches())
62 {
63 return true;
64 }
65 return false;
66 }
67 };*/
68
69 /*
70 private FileFilter _resourceFileFilter = new FileFilter()
71 {
72 public boolean accept(File pathname)
73 {
74 if (pathname.isDirectory() && RESOURCE_VERSION_CHECKER.matcher(pathname.getName()).matches())
75 {
76 return true;
77 }
78 return false;
79 }
80 };*/
81
82 private final boolean _developmentStage;
83
84 public ClassLoaderResourceLoader(String prefix)
85 {
86 super(prefix);
87 _developmentStage = FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development);
88 }
89
90 @Override
91 public String getLibraryVersion(String path)
92 {
93 return null;
94 /*
95 String libraryVersion = null;
96 if (getPrefix() != null)
97 path = getPrefix() + '/' + path;
98
99 URL url = getClassLoader().getResource(path);
100
101 if (url == null)
102 {
103 // This library does not exists for this
104 // ResourceLoader
105 return null;
106 }
107
108 // The problem here is how to scan the directory. When a ClassLoader
109 // is used two cases could occur
110 // 1. The files are unpacked so we can use Url.toURI and crawl
111 // the directory using the api for files.
112 // 2. The files are packed in a jar. This case is more tricky,
113 // because we only have a URL. Checking the jar api we can use
114 // JarURLConnection (Sounds strange, but the api of
115 // URL.openConnection says that for a jar connection a
116 // JarURLConnection is returned). From this point we can access
117 // to the jar api and solve the algoritm.
118 if (url.getProtocol().equals("file"))
119 {
120 try
121 {
122 File directory = new File(url.toURI());
123 if (directory.isDirectory())
124 {
125 File[] versions = directory.listFiles(_libraryFileFilter);
126 for (int i = 0; i < versions.length; i++)
127 {
128 String version = versions[i].getName();
129 if (VERSION_CHECKER.matcher(version).matches())
130 {
131 if (libraryVersion == null)
132 {
133 libraryVersion = version;
134 }
135 else if (getVersionComparator().compare(libraryVersion, version) < 0)
136 {
137 libraryVersion = version;
138 }
139 }
140 }
141 }
142 }
143 catch (URISyntaxException e)
144 {
145 // Just return null, because library version cannot be
146 // resolved.
147 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
148 if (log.isLoggable(Level.WARNING))
149 {
150 log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "+e.getMessage(), e);
151 }
152 }
153 }
154 else if (isJarResourceProtocol(url.getProtocol()))
155 {
156 try
157 {
158 url = getClassLoader().getResource(path + '/');
159
160 if (url != null)
161 {
162 JarURLConnection conn = (JarURLConnection)url.openConnection();
163 // See DIGESTER-29 for related problem
164 conn.setUseCaches(false);
165
166 try
167 {
168 if (conn.getJarEntry().isDirectory())
169 {
170 // Unfortunately, we have to scan all entry files
171 // because there is no proper api to scan it as a
172 // directory tree.
173 JarFile file = conn.getJarFile();
174 for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
175 {
176 JarEntry entry = en.nextElement();
177 String entryName = entry.getName();
178
179 if (entryName.startsWith(path + '/'))
180 {
181 if (entryName.length() == path.length() + 1)
182 {
183 // the same string, just skip it
184 continue;
185 }
186
187 if (entryName.charAt(entryName.length() - 1) != '/')
188 {
189 // Skip files
190 continue;
191 }
192
193 entryName = entryName.substring(path.length() + 1, entryName.length() - 1);
194
195 if (entryName.indexOf('/') >= 0)
196 {
197 // Inner Directory
198 continue;
199 }
200
201 String version = entryName;
202 if (VERSION_CHECKER.matcher(version).matches())
203 {
204 if (libraryVersion == null)
205 {
206 libraryVersion = version;
207 }
208 else if (getVersionComparator().compare(libraryVersion, version) < 0)
209 {
210 libraryVersion = version;
211 }
212 }
213 }
214 }
215 }
216 }
217 finally
218 {
219 //See TRINIDAD-73
220 //just close the input stream again if
221 //by inspecting the entries the stream
222 //was let open.
223 try
224 {
225 conn.getInputStream().close();
226 }
227 catch (Exception exception)
228 {
229 // Ignored
230 }
231 }
232 }
233 }
234 catch (IOException e)
235 {
236 // Just return null, because library version cannot be
237 // resolved.
238 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
239 if (log.isLoggable(Level.WARNING))
240 {
241 log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
242 }
243 }
244 }
245 return libraryVersion;
246 */
247 }
248
249 @Override
250 public InputStream getResourceInputStream(ResourceMeta resourceMeta)
251 {
252 InputStream is = null;
253 if (getPrefix() != null && !"".equals(getPrefix()))
254 {
255 String name = getPrefix() + '/' + resourceMeta.getResourceIdentifier();
256 is = getClassLoader().getResourceAsStream(name);
257 if (is == null)
258 {
259 is = this.getClass().getClassLoader().getResourceAsStream(name);
260 }
261 return is;
262 }
263 else
264 {
265 is = getClassLoader().getResourceAsStream(resourceMeta.getResourceIdentifier());
266 if (is == null)
267 {
268 is = this.getClass().getClassLoader().getResourceAsStream(resourceMeta.getResourceIdentifier());
269 }
270 return is;
271 }
272 }
273
274 @Override
275 public URL getResourceURL(ResourceMeta resourceMeta)
276 {
277 URL url = null;
278 if (getPrefix() != null && !"".equals(getPrefix()))
279 {
280 String name = getPrefix() + '/' + resourceMeta.getResourceIdentifier();
281 url = getClassLoader().getResource(name);
282 if (url == null)
283 {
284 url = this.getClass().getClassLoader().getResource(name);
285 }
286 return url;
287 }
288 else
289 {
290 url = getClassLoader().getResource(resourceMeta.getResourceIdentifier());
291 if (url == null)
292 {
293 url = this.getClass().getClassLoader().getResource(resourceMeta.getResourceIdentifier());
294 }
295 return url;
296 }
297 }
298
299 @Override
300 public String getResourceVersion(String path)
301 {
302 return null;
303 /*
304 String resourceVersion = null;
305
306 if (getPrefix() != null)
307 path = getPrefix() + '/' + path;
308
309 URL url = getClassLoader().getResource(path);
310
311 if (url == null)
312 {
313 // This library does not exists for this
314 // ResourceLoader
315 return null;
316 }
317
318 if (url.getProtocol().equals("file"))
319 {
320 try
321 {
322 File directory = new File(url.toURI());
323 if (directory.isDirectory())
324 {
325 File[] versions = directory.listFiles(_resourceFileFilter);
326 for (int i = 0; i < versions.length; i++)
327 {
328 String version = versions[i].getName();
329 if (resourceVersion == null)
330 {
331 resourceVersion = version;
332 }
333 else if (getVersionComparator().compare(resourceVersion, version) < 0)
334 {
335 resourceVersion = version;
336 }
337 }
338 //Since it is a directory and no version found set resourceVersion as invalid
339 if (resourceVersion == null)
340 {
341 resourceVersion = VERSION_INVALID;
342 }
343 }
344 }
345 catch (URISyntaxException e)
346 {
347 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
348 if (log.isLoggable(Level.WARNING))
349 {
350 log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "+e.getMessage(), e);
351 }
352 }
353 }
354 else if (isJarResourceProtocol(url.getProtocol()))
355 {
356 try
357 {
358 url = getClassLoader().getResource(path + '/');
359
360 if (url != null)
361 {
362 JarURLConnection conn = (JarURLConnection)url.openConnection();
363 // See DIGESTER-29 for related problem
364 conn.setUseCaches(false);
365
366 try
367 {
368 if (conn.getJarEntry().isDirectory())
369 {
370 // Unfortunately, we have to scan all entry files
371 JarFile file = conn.getJarFile();
372 for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
373 {
374 JarEntry entry = en.nextElement();
375 String entryName = entry.getName();
376
377 if (entryName.startsWith(path + '/'))
378 {
379 if (entryName.length() == path.length() + 1)
380 {
381 // the same string, just skip it
382 continue;
383 }
384
385 entryName = entryName.substring(path.length());
386 if (RESOURCE_VERSION_CHECKER.matcher(entryName).matches())
387 {
388 String version = entryName.substring(1, entryName.lastIndexOf('.'));
389 if (resourceVersion == null)
390 {
391 resourceVersion = version;
392 }
393 else if (getVersionComparator().compare(resourceVersion, version) < 0)
394 {
395 resourceVersion = version;
396 }
397 }
398 }
399 }
400 if (resourceVersion == null)
401 {
402 resourceVersion = VERSION_INVALID;
403 }
404 }
405 }
406 finally
407 {
408 //See TRINIDAD-73
409 //just close the input stream again if
410 //by inspecting the entries the stream
411 //was let open.
412 try
413 {
414 conn.getInputStream().close();
415 }
416 catch (Exception exception)
417 {
418 // Ignored
419 }
420 }
421
422 }
423 }
424 catch (IOException e)
425 {
426 // Just return null, because library version cannot be
427 // resolved.
428 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
429 if (log.isLoggable(Level.WARNING))
430 {
431 log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
432 }
433 }
434 }
435 return resourceVersion;
436 */
437 }
438
439 @Override
440 public ResourceMeta createResourceMeta(String prefix, String libraryName, String libraryVersion,
441 String resourceName, String resourceVersion)
442 {
443 //if (_developmentStage && libraryName != null &&
444 // JAVAX_FACES_LIBRARY_NAME.equals(libraryName) &&
445 // JSF_JS_RESOURCE_NAME.equals(resourceName))
446 //{
447 // InternalClassLoaderResourceLoader will serve it, so return null in this case.
448 // return null;
449 //} else if (_developmentStage && libraryName != null &&
450 // MYFACES_LIBRARY_NAME.equals(libraryName) &&
451 // MYFACES_JS_RESOURCE_NAME.equals(resourceName)) {
452 // InternalClassLoaderResourceLoader will serve it, so return null in this case.
453 // return null;
454 //} else
455 //{
456 return new ResourceMetaImpl(prefix, libraryName, libraryVersion, resourceName, resourceVersion);
457 //}
458 }
459
460 /**
461 * Returns the ClassLoader to use when looking up resources under the top level package. By default, this is the
462 * context class loader.
463 *
464 * @return the ClassLoader used to lookup resources
465 */
466 protected ClassLoader getClassLoader()
467 {
468 return ClassUtils.getContextClassLoader();
469 }
470
471 @Override
472 public boolean libraryExists(String libraryName)
473 {
474 if (getPrefix() != null && !"".equals(getPrefix()))
475 {
476 URL url = getClassLoader().getResource(getPrefix() + '/' + libraryName);
477 if (url == null)
478 {
479 url = this.getClass().getClassLoader().getResource(getPrefix() + '/' + libraryName);
480 }
481 if (url != null)
482 {
483 return true;
484 }
485 }
486 else
487 {
488 URL url = getClassLoader().getResource(libraryName);
489 if (url == null)
490 {
491 url = this.getClass().getClassLoader().getResource(libraryName);
492 }
493 if (url != null)
494 {
495 return true;
496 }
497 }
498 return false;
499 }
500
501 /**
502 * <p>Determines whether the given URL resource protocol refers to a JAR file. Note that
503 * BEA WebLogic and IBM WebSphere don't use the "jar://" protocol for some reason even
504 * though you can treat these resources just like normal JAR files, i.e. you can ignore
505 * the difference between these protocols after this method has returned.</p>
506 *
507 * @param protocol the URL resource protocol you want to check
508 *
509 * @return <code>true</code> if the given URL resource protocol refers to a JAR file,
510 * <code>false</code> otherwise
511 */
512 /*
513 private static boolean isJarResourceProtocol(String protocol)
514 {
515 // Websphere uses the protocol "wsjar://" and Weblogic uses the protocol "zip://".
516 return "jar".equals(protocol) || "wsjar".equals(protocol) || "zip".equals(protocol);
517 }*/
518
519 }