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 }