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.shared.resource; 20 21 import java.io.File; 22 import java.io.FileFilter; 23 import java.io.InputStream; 24 import java.net.URL; 25 import java.util.Iterator; 26 import javax.faces.application.ResourceVisitOption; 27 import javax.faces.context.FacesContext; 28 29 import org.apache.myfaces.shared.util.ClassUtils; 30 31 /** 32 * A resource loader implementation which loads resources from the thread ClassLoader. 33 * 34 */ 35 public class ClassLoaderResourceLoader extends ResourceLoader 36 { 37 /** 38 * It checks version like this: 1, 1_0, 1_0_0, 100_100 39 * 40 * Used on getLibraryVersion to filter resource directories 41 **/ 42 //protected static Pattern VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*"); 43 44 /** 45 * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js 46 * 47 * Used on getResourceVersion to filter resources 48 **/ 49 //protected static Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*"); 50 51 /* 52 private FileFilter _libraryFileFilter = new FileFilter() 53 { 54 public boolean accept(File pathname) 55 { 56 if (pathname.isDirectory() && VERSION_CHECKER.matcher(pathname.getName()).matches()) 57 { 58 return true; 59 } 60 return false; 61 } 62 };*/ 63 64 /* 65 private FileFilter _resourceFileFilter = new FileFilter() 66 { 67 public boolean accept(File pathname) 68 { 69 if (pathname.isDirectory() && RESOURCE_VERSION_CHECKER.matcher(pathname.getName()).matches()) 70 { 71 return true; 72 } 73 return false; 74 } 75 };*/ 76 77 private FileFilter _resourceFileFilter = new FileFilter() 78 { 79 public boolean accept(File pathname) 80 { 81 return true; 82 } 83 }; 84 85 public ClassLoaderResourceLoader(String prefix) 86 { 87 super(prefix); 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(String resourceId) 276 { 277 URL url = null; 278 if (getPrefix() != null && !"".equals(getPrefix())) 279 { 280 String name = getPrefix() + '/' + resourceId; 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(resourceId); 291 if (url == null) 292 { 293 url = this.getClass().getClassLoader().getResource(resourceId); 294 } 295 return url; 296 } 297 } 298 299 @Override 300 public URL getResourceURL(ResourceMeta resourceMeta) 301 { 302 return getResourceURL(resourceMeta.getResourceIdentifier()); 303 } 304 305 @Override 306 public String getResourceVersion(String path) 307 { 308 return null; 309 /* 310 String resourceVersion = null; 311 312 if (getPrefix() != null) 313 path = getPrefix() + '/' + path; 314 315 URL url = getClassLoader().getResource(path); 316 317 if (url == null) 318 { 319 // This library does not exists for this 320 // ResourceLoader 321 return null; 322 } 323 324 if (url.getProtocol().equals("file")) 325 { 326 try 327 { 328 File directory = new File(url.toURI()); 329 if (directory.isDirectory()) 330 { 331 File[] versions = directory.listFiles(_resourceFileFilter); 332 for (int i = 0; i < versions.length; i++) 333 { 334 String version = versions[i].getName(); 335 if (resourceVersion == null) 336 { 337 resourceVersion = version; 338 } 339 else if (getVersionComparator().compare(resourceVersion, version) < 0) 340 { 341 resourceVersion = version; 342 } 343 } 344 //Since it is a directory and no version found set resourceVersion as invalid 345 if (resourceVersion == null) 346 { 347 resourceVersion = VERSION_INVALID; 348 } 349 } 350 } 351 catch (URISyntaxException e) 352 { 353 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName()); 354 if (log.isLoggable(Level.WARNING)) 355 { 356 log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "+e.getMessage(), e); 357 } 358 } 359 } 360 else if (isJarResourceProtocol(url.getProtocol())) 361 { 362 try 363 { 364 url = getClassLoader().getResource(path + '/'); 365 366 if (url != null) 367 { 368 JarURLConnection conn = (JarURLConnection)url.openConnection(); 369 // See DIGESTER-29 for related problem 370 conn.setUseCaches(false); 371 372 try 373 { 374 if (conn.getJarEntry().isDirectory()) 375 { 376 // Unfortunately, we have to scan all entry files 377 JarFile file = conn.getJarFile(); 378 for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();) 379 { 380 JarEntry entry = en.nextElement(); 381 String entryName = entry.getName(); 382 383 if (entryName.startsWith(path + '/')) 384 { 385 if (entryName.length() == path.length() + 1) 386 { 387 // the same string, just skip it 388 continue; 389 } 390 391 entryName = entryName.substring(path.length()); 392 if (RESOURCE_VERSION_CHECKER.matcher(entryName).matches()) 393 { 394 String version = entryName.substring(1, entryName.lastIndexOf('.')); 395 if (resourceVersion == null) 396 { 397 resourceVersion = version; 398 } 399 else if (getVersionComparator().compare(resourceVersion, version) < 0) 400 { 401 resourceVersion = version; 402 } 403 } 404 } 405 } 406 if (resourceVersion == null) 407 { 408 resourceVersion = VERSION_INVALID; 409 } 410 } 411 } 412 finally 413 { 414 //See TRINIDAD-73 415 //just close the input stream again if 416 //by inspecting the entries the stream 417 //was let open. 418 try 419 { 420 conn.getInputStream().close(); 421 } 422 catch (Exception exception) 423 { 424 // Ignored 425 } 426 } 427 428 } 429 } 430 catch (IOException e) 431 { 432 // Just return null, because library version cannot be 433 // resolved. 434 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName()); 435 if (log.isLoggable(Level.WARNING)) 436 { 437 log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e); 438 } 439 } 440 } 441 return resourceVersion; 442 */ 443 } 444 445 @Override 446 public ResourceMeta createResourceMeta(String prefix, String libraryName, String libraryVersion, 447 String resourceName, String resourceVersion) 448 { 449 //if (_developmentStage && libraryName != null && 450 // ResourceUtils.JAVAX_FACES_LIBRARY_NAME.equals(libraryName) && 451 // ResourceUtils.JSF_JS_RESOURCE_NAME.equals(resourceName)) 452 //{ 453 // InternalClassLoaderResourceLoader will serve it, so return null in this case. 454 // return null; 455 //} else if (_developmentStage && libraryName != null && 456 // ResourceUtils.MYFACES_LIBRARY_NAME.equals(libraryName) && 457 // ResourceUtils.MYFACES_JS_RESOURCE_NAME.equals(resourceName)) { 458 // InternalClassLoaderResourceLoader will serve it, so return null in this case. 459 // return null; 460 //} else 461 //{ 462 return new ResourceMetaImpl(prefix, libraryName, libraryVersion, resourceName, resourceVersion); 463 //} 464 } 465 466 /** 467 * Returns the ClassLoader to use when looking up resources under the top level package. By default, this is the 468 * context class loader. 469 * 470 * @return the ClassLoader used to lookup resources 471 */ 472 protected ClassLoader getClassLoader() 473 { 474 return ClassUtils.getContextClassLoader(); 475 } 476 477 @Override 478 public boolean libraryExists(String libraryName) 479 { 480 if (getPrefix() != null && !"".equals(getPrefix())) 481 { 482 URL url = getClassLoader().getResource(getPrefix() + '/' + libraryName); 483 if (url == null) 484 { 485 url = this.getClass().getClassLoader().getResource(getPrefix() + '/' + libraryName); 486 } 487 if (url != null) 488 { 489 return true; 490 } 491 } 492 else 493 { 494 URL url = getClassLoader().getResource(libraryName); 495 if (url == null) 496 { 497 url = this.getClass().getClassLoader().getResource(libraryName); 498 } 499 if (url != null) 500 { 501 return true; 502 } 503 } 504 return false; 505 } 506 507 @Override 508 public Iterator<String> iterator(FacesContext facesContext, String path, 509 int maxDepth, ResourceVisitOption... options) 510 { 511 String basePath = path; 512 513 if (getPrefix() != null) 514 { 515 basePath = getPrefix() + '/' + (path.startsWith("/") ? path.substring(1) : path); 516 } 517 518 URL url = getClassLoader().getResource(basePath); 519 520 return new ClassLoaderResourceLoaderIterator(url, basePath, maxDepth, options); 521 } 522 523 }