001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.xbean.finder; 018 019 import java.io.BufferedInputStream; 020 import java.io.File; 021 import java.io.IOException; 022 import java.io.InputStream; 023 import java.net.HttpURLConnection; 024 import java.net.JarURLConnection; 025 import java.net.MalformedURLException; 026 import java.net.URL; 027 import java.net.URLConnection; 028 import java.net.URLDecoder; 029 import java.util.ArrayList; 030 import java.util.Collections; 031 import java.util.Enumeration; 032 import java.util.HashMap; 033 import java.util.Iterator; 034 import java.util.List; 035 import java.util.Map; 036 import java.util.Properties; 037 import java.util.Vector; 038 import java.util.jar.JarEntry; 039 import java.util.jar.JarFile; 040 041 /** 042 * @author David Blevins 043 * @version $Rev: 936567 $ $Date: 2010-04-22 01:41:32 +0200 (Thu, 22 Apr 2010) $ 044 */ 045 public class ResourceFinder { 046 047 private final URL[] urls; 048 private final String path; 049 private final ClassLoader classLoader; 050 private final List<String> resourcesNotLoaded = new ArrayList<String>(); 051 052 public ResourceFinder(URL... urls) { 053 this(null, Thread.currentThread().getContextClassLoader(), urls); 054 } 055 056 public ResourceFinder(String path) { 057 this(path, Thread.currentThread().getContextClassLoader(), null); 058 } 059 060 public ResourceFinder(String path, URL... urls) { 061 this(path, Thread.currentThread().getContextClassLoader(), urls); 062 } 063 064 public ResourceFinder(String path, ClassLoader classLoader) { 065 this(path, classLoader, null); 066 } 067 068 public ResourceFinder(String path, ClassLoader classLoader, URL... urls) { 069 if (path == null){ 070 path = ""; 071 } else if (path.length() > 0 && !path.endsWith("/")) { 072 path += "/"; 073 } 074 this.path = path; 075 076 if (classLoader == null) { 077 classLoader = Thread.currentThread().getContextClassLoader(); 078 } 079 this.classLoader = classLoader; 080 081 for (int i = 0; urls != null && i < urls.length; i++) { 082 URL url = urls[i]; 083 if (url == null || isDirectory(url) || url.getProtocol().equals("jar")) { 084 continue; 085 } 086 try { 087 urls[i] = new URL("jar", "", -1, url.toString() + "!/"); 088 } catch (MalformedURLException e) { 089 } 090 } 091 this.urls = (urls == null || urls.length == 0)? null : urls; 092 } 093 094 private static boolean isDirectory(URL url) { 095 String file = url.getFile(); 096 return (file.length() > 0 && file.charAt(file.length() - 1) == '/'); 097 } 098 099 /** 100 * Returns a list of resources that could not be loaded in the last invoked findAvailable* or 101 * mapAvailable* methods. 102 * <p/> 103 * The list will only contain entries of resources that match the requirements 104 * of the last invoked findAvailable* or mapAvailable* methods, but were unable to be 105 * loaded and included in their results. 106 * <p/> 107 * The list returned is unmodifiable and the results of this method will change 108 * after each invocation of a findAvailable* or mapAvailable* methods. 109 * <p/> 110 * This method is not thread safe. 111 */ 112 public List<String> getResourcesNotLoaded() { 113 return Collections.unmodifiableList(resourcesNotLoaded); 114 } 115 116 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 117 // 118 // Find 119 // 120 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 121 122 public URL find(String uri) throws IOException { 123 String fullUri = path + uri; 124 125 URL resource = getResource(fullUri); 126 if (resource == null) { 127 throw new IOException("Could not find resource '" + fullUri + "'"); 128 } 129 130 return resource; 131 } 132 133 public List<URL> findAll(String uri) throws IOException { 134 String fullUri = path + uri; 135 136 Enumeration<URL> resources = getResources(fullUri); 137 List<URL> list = new ArrayList(); 138 while (resources.hasMoreElements()) { 139 URL url = resources.nextElement(); 140 list.add(url); 141 } 142 return list; 143 } 144 145 146 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 147 // 148 // Find String 149 // 150 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 151 152 /** 153 * Reads the contents of the URL as a {@link String}'s and returns it. 154 * 155 * @param uri 156 * @return a stringified content of a resource 157 * @throws IOException if a resource pointed out by the uri param could not be find 158 * @see ClassLoader#getResource(String) 159 */ 160 public String findString(String uri) throws IOException { 161 String fullUri = path + uri; 162 163 URL resource = getResource(fullUri); 164 if (resource == null) { 165 throw new IOException("Could not find a resource in : " + fullUri); 166 } 167 168 return readContents(resource); 169 } 170 171 /** 172 * Reads the contents of the found URLs as a list of {@link String}'s and returns them. 173 * 174 * @param uri 175 * @return a list of the content of each resource URL found 176 * @throws IOException if any of the found URLs are unable to be read. 177 */ 178 public List<String> findAllStrings(String uri) throws IOException { 179 String fulluri = path + uri; 180 181 List<String> strings = new ArrayList<String>(); 182 183 Enumeration<URL> resources = getResources(fulluri); 184 while (resources.hasMoreElements()) { 185 URL url = resources.nextElement(); 186 String string = readContents(url); 187 strings.add(string); 188 } 189 return strings; 190 } 191 192 /** 193 * Reads the contents of the found URLs as a Strings and returns them. 194 * Individual URLs that cannot be read are skipped and added to the 195 * list of 'resourcesNotLoaded' 196 * 197 * @param uri 198 * @return a list of the content of each resource URL found 199 * @throws IOException if classLoader.getResources throws an exception 200 */ 201 public List<String> findAvailableStrings(String uri) throws IOException { 202 resourcesNotLoaded.clear(); 203 String fulluri = path + uri; 204 205 List<String> strings = new ArrayList<String>(); 206 207 Enumeration<URL> resources = getResources(fulluri); 208 while (resources.hasMoreElements()) { 209 URL url = resources.nextElement(); 210 try { 211 String string = readContents(url); 212 strings.add(string); 213 } catch (IOException notAvailable) { 214 resourcesNotLoaded.add(url.toExternalForm()); 215 } 216 } 217 return strings; 218 } 219 220 /** 221 * Reads the contents of all non-directory URLs immediately under the specified 222 * location and returns them in a map keyed by the file name. 223 * <p/> 224 * Any URLs that cannot be read will cause an exception to be thrown. 225 * <p/> 226 * Example classpath: 227 * <p/> 228 * META-INF/serializables/one 229 * META-INF/serializables/two 230 * META-INF/serializables/three 231 * META-INF/serializables/four/foo.txt 232 * <p/> 233 * ResourceFinder finder = new ResourceFinder("META-INF/"); 234 * Map map = finder.mapAvailableStrings("serializables"); 235 * map.contains("one"); // true 236 * map.contains("two"); // true 237 * map.contains("three"); // true 238 * map.contains("four"); // false 239 * 240 * @param uri 241 * @return a list of the content of each resource URL found 242 * @throws IOException if any of the urls cannot be read 243 */ 244 public Map<String, String> mapAllStrings(String uri) throws IOException { 245 Map<String, String> strings = new HashMap<String, String>(); 246 Map<String, URL> resourcesMap = getResourcesMap(uri); 247 for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) { 248 Map.Entry entry = (Map.Entry) iterator.next(); 249 String name = (String) entry.getKey(); 250 URL url = (URL) entry.getValue(); 251 String value = readContents(url); 252 strings.put(name, value); 253 } 254 return strings; 255 } 256 257 /** 258 * Reads the contents of all non-directory URLs immediately under the specified 259 * location and returns them in a map keyed by the file name. 260 * <p/> 261 * Individual URLs that cannot be read are skipped and added to the 262 * list of 'resourcesNotLoaded' 263 * <p/> 264 * Example classpath: 265 * <p/> 266 * META-INF/serializables/one 267 * META-INF/serializables/two # not readable 268 * META-INF/serializables/three 269 * META-INF/serializables/four/foo.txt 270 * <p/> 271 * ResourceFinder finder = new ResourceFinder("META-INF/"); 272 * Map map = finder.mapAvailableStrings("serializables"); 273 * map.contains("one"); // true 274 * map.contains("two"); // false 275 * map.contains("three"); // true 276 * map.contains("four"); // false 277 * 278 * @param uri 279 * @return a list of the content of each resource URL found 280 * @throws IOException if classLoader.getResources throws an exception 281 */ 282 public Map<String, String> mapAvailableStrings(String uri) throws IOException { 283 resourcesNotLoaded.clear(); 284 Map<String, String> strings = new HashMap<String, String>(); 285 Map<String, URL> resourcesMap = getResourcesMap(uri); 286 for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) { 287 Map.Entry entry = (Map.Entry) iterator.next(); 288 String name = (String) entry.getKey(); 289 URL url = (URL) entry.getValue(); 290 try { 291 String value = readContents(url); 292 strings.put(name, value); 293 } catch (IOException notAvailable) { 294 resourcesNotLoaded.add(url.toExternalForm()); 295 } 296 } 297 return strings; 298 } 299 300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 301 // 302 // Find Class 303 // 304 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 305 306 /** 307 * Executes {@link #findString(String)} assuming the contents URL found is the name of 308 * a class that should be loaded and returned. 309 * 310 * @param uri 311 * @return 312 * @throws IOException 313 * @throws ClassNotFoundException 314 */ 315 public Class findClass(String uri) throws IOException, ClassNotFoundException { 316 String className = findString(uri); 317 return (Class) classLoader.loadClass(className); 318 } 319 320 /** 321 * Executes findAllStrings assuming the strings are 322 * the names of a classes that should be loaded and returned. 323 * <p/> 324 * Any URL or class that cannot be loaded will cause an exception to be thrown. 325 * 326 * @param uri 327 * @return 328 * @throws IOException 329 * @throws ClassNotFoundException 330 */ 331 public List<Class> findAllClasses(String uri) throws IOException, ClassNotFoundException { 332 List<Class> classes = new ArrayList<Class>(); 333 List<String> strings = findAllStrings(uri); 334 for (String className : strings) { 335 Class clazz = classLoader.loadClass(className); 336 classes.add(clazz); 337 } 338 return classes; 339 } 340 341 /** 342 * Executes findAvailableStrings assuming the strings are 343 * the names of a classes that should be loaded and returned. 344 * <p/> 345 * Any class that cannot be loaded will be skipped and placed in the 346 * 'resourcesNotLoaded' collection. 347 * 348 * @param uri 349 * @return 350 * @throws IOException if classLoader.getResources throws an exception 351 */ 352 public List<Class> findAvailableClasses(String uri) throws IOException { 353 resourcesNotLoaded.clear(); 354 List<Class> classes = new ArrayList<Class>(); 355 List<String> strings = findAvailableStrings(uri); 356 for (String className : strings) { 357 try { 358 Class clazz = classLoader.loadClass(className); 359 classes.add(clazz); 360 } catch (Exception notAvailable) { 361 resourcesNotLoaded.add(className); 362 } 363 } 364 return classes; 365 } 366 367 /** 368 * Executes mapAllStrings assuming the value of each entry in the 369 * map is the name of a class that should be loaded. 370 * <p/> 371 * Any class that cannot be loaded will be cause an exception to be thrown. 372 * <p/> 373 * Example classpath: 374 * <p/> 375 * META-INF/xmlparsers/xerces 376 * META-INF/xmlparsers/crimson 377 * <p/> 378 * ResourceFinder finder = new ResourceFinder("META-INF/"); 379 * Map map = finder.mapAvailableStrings("xmlparsers"); 380 * map.contains("xerces"); // true 381 * map.contains("crimson"); // true 382 * Class xercesClass = map.get("xerces"); 383 * Class crimsonClass = map.get("crimson"); 384 * 385 * @param uri 386 * @return 387 * @throws IOException 388 * @throws ClassNotFoundException 389 */ 390 public Map<String, Class> mapAllClasses(String uri) throws IOException, ClassNotFoundException { 391 Map<String, Class> classes = new HashMap<String, Class>(); 392 Map<String, String> map = mapAllStrings(uri); 393 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 394 Map.Entry entry = (Map.Entry) iterator.next(); 395 String string = (String) entry.getKey(); 396 String className = (String) entry.getValue(); 397 Class clazz = classLoader.loadClass(className); 398 classes.put(string, clazz); 399 } 400 return classes; 401 } 402 403 /** 404 * Executes mapAvailableStrings assuming the value of each entry in the 405 * map is the name of a class that should be loaded. 406 * <p/> 407 * Any class that cannot be loaded will be skipped and placed in the 408 * 'resourcesNotLoaded' collection. 409 * <p/> 410 * Example classpath: 411 * <p/> 412 * META-INF/xmlparsers/xerces 413 * META-INF/xmlparsers/crimson 414 * <p/> 415 * ResourceFinder finder = new ResourceFinder("META-INF/"); 416 * Map map = finder.mapAvailableStrings("xmlparsers"); 417 * map.contains("xerces"); // true 418 * map.contains("crimson"); // true 419 * Class xercesClass = map.get("xerces"); 420 * Class crimsonClass = map.get("crimson"); 421 * 422 * @param uri 423 * @return 424 * @throws IOException if classLoader.getResources throws an exception 425 */ 426 public Map<String, Class> mapAvailableClasses(String uri) throws IOException { 427 resourcesNotLoaded.clear(); 428 Map<String, Class> classes = new HashMap<String, Class>(); 429 Map<String, String> map = mapAvailableStrings(uri); 430 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 431 Map.Entry entry = (Map.Entry) iterator.next(); 432 String string = (String) entry.getKey(); 433 String className = (String) entry.getValue(); 434 try { 435 Class clazz = classLoader.loadClass(className); 436 classes.put(string, clazz); 437 } catch (Exception notAvailable) { 438 resourcesNotLoaded.add(className); 439 } 440 } 441 return classes; 442 } 443 444 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 445 // 446 // Find Implementation 447 // 448 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 449 450 /** 451 * Assumes the class specified points to a file in the classpath that contains 452 * the name of a class that implements or is a subclass of the specfied class. 453 * <p/> 454 * Any class that cannot be loaded will be cause an exception to be thrown. 455 * <p/> 456 * Example classpath: 457 * <p/> 458 * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream 459 * META-INF/java.io.OutputStream 460 * <p/> 461 * ResourceFinder finder = new ResourceFinder("META-INF/"); 462 * Class clazz = finder.findImplementation(java.io.InputStream.class); 463 * clazz.getName(); // returns "org.acme.AcmeInputStream" 464 * 465 * @param interfase a superclass or interface 466 * @return 467 * @throws IOException if the URL cannot be read 468 * @throws ClassNotFoundException if the class found is not loadable 469 * @throws ClassCastException if the class found is not assignable to the specified superclass or interface 470 */ 471 public Class findImplementation(Class interfase) throws IOException, ClassNotFoundException { 472 String className = findString(interfase.getName()); 473 Class impl = classLoader.loadClass(className); 474 if (!interfase.isAssignableFrom(impl)) { 475 throw new ClassCastException("Class not of type: " + interfase.getName()); 476 } 477 return impl; 478 } 479 480 /** 481 * Assumes the class specified points to a file in the classpath that contains 482 * the name of a class that implements or is a subclass of the specfied class. 483 * <p/> 484 * Any class that cannot be loaded or assigned to the specified interface will be cause 485 * an exception to be thrown. 486 * <p/> 487 * Example classpath: 488 * <p/> 489 * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream 490 * META-INF/java.io.InputStream # contains the classname org.widget.NeatoInputStream 491 * META-INF/java.io.InputStream # contains the classname com.foo.BarInputStream 492 * <p/> 493 * ResourceFinder finder = new ResourceFinder("META-INF/"); 494 * List classes = finder.findAllImplementations(java.io.InputStream.class); 495 * classes.contains("org.acme.AcmeInputStream"); // true 496 * classes.contains("org.widget.NeatoInputStream"); // true 497 * classes.contains("com.foo.BarInputStream"); // true 498 * 499 * @param interfase a superclass or interface 500 * @return 501 * @throws IOException if the URL cannot be read 502 * @throws ClassNotFoundException if the class found is not loadable 503 * @throws ClassCastException if the class found is not assignable to the specified superclass or interface 504 */ 505 public List<Class> findAllImplementations(Class interfase) throws IOException, ClassNotFoundException { 506 List<Class> implementations = new ArrayList<Class>(); 507 List<String> strings = findAllStrings(interfase.getName()); 508 for (String className : strings) { 509 Class impl = classLoader.loadClass(className); 510 if (!interfase.isAssignableFrom(impl)) { 511 throw new ClassCastException("Class not of type: " + interfase.getName()); 512 } 513 implementations.add(impl); 514 } 515 return implementations; 516 } 517 518 /** 519 * Assumes the class specified points to a file in the classpath that contains 520 * the name of a class that implements or is a subclass of the specfied class. 521 * <p/> 522 * Any class that cannot be loaded or are not assignable to the specified class will be 523 * skipped and placed in the 'resourcesNotLoaded' collection. 524 * <p/> 525 * Example classpath: 526 * <p/> 527 * META-INF/java.io.InputStream # contains the classname org.acme.AcmeInputStream 528 * META-INF/java.io.InputStream # contains the classname org.widget.NeatoInputStream 529 * META-INF/java.io.InputStream # contains the classname com.foo.BarInputStream 530 * <p/> 531 * ResourceFinder finder = new ResourceFinder("META-INF/"); 532 * List classes = finder.findAllImplementations(java.io.InputStream.class); 533 * classes.contains("org.acme.AcmeInputStream"); // true 534 * classes.contains("org.widget.NeatoInputStream"); // true 535 * classes.contains("com.foo.BarInputStream"); // true 536 * 537 * @param interfase a superclass or interface 538 * @return 539 * @throws IOException if classLoader.getResources throws an exception 540 */ 541 public List<Class> findAvailableImplementations(Class interfase) throws IOException { 542 resourcesNotLoaded.clear(); 543 List<Class> implementations = new ArrayList<Class>(); 544 List<String> strings = findAvailableStrings(interfase.getName()); 545 for (String className : strings) { 546 try { 547 Class impl = classLoader.loadClass(className); 548 if (interfase.isAssignableFrom(impl)) { 549 implementations.add(impl); 550 } else { 551 resourcesNotLoaded.add(className); 552 } 553 } catch (Exception notAvailable) { 554 resourcesNotLoaded.add(className); 555 } 556 } 557 return implementations; 558 } 559 560 /** 561 * Assumes the class specified points to a directory in the classpath that holds files 562 * containing the name of a class that implements or is a subclass of the specfied class. 563 * <p/> 564 * Any class that cannot be loaded or assigned to the specified interface will be cause 565 * an exception to be thrown. 566 * <p/> 567 * Example classpath: 568 * <p/> 569 * META-INF/java.net.URLStreamHandler/jar 570 * META-INF/java.net.URLStreamHandler/file 571 * META-INF/java.net.URLStreamHandler/http 572 * <p/> 573 * ResourceFinder finder = new ResourceFinder("META-INF/"); 574 * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class); 575 * Class jarUrlHandler = map.get("jar"); 576 * Class fileUrlHandler = map.get("file"); 577 * Class httpUrlHandler = map.get("http"); 578 * 579 * @param interfase a superclass or interface 580 * @return 581 * @throws IOException if the URL cannot be read 582 * @throws ClassNotFoundException if the class found is not loadable 583 * @throws ClassCastException if the class found is not assignable to the specified superclass or interface 584 */ 585 public Map<String, Class> mapAllImplementations(Class interfase) throws IOException, ClassNotFoundException { 586 Map<String, Class> implementations = new HashMap<String, Class>(); 587 Map<String, String> map = mapAllStrings(interfase.getName()); 588 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 589 Map.Entry entry = (Map.Entry) iterator.next(); 590 String string = (String) entry.getKey(); 591 String className = (String) entry.getValue(); 592 Class impl = classLoader.loadClass(className); 593 if (!interfase.isAssignableFrom(impl)) { 594 throw new ClassCastException("Class not of type: " + interfase.getName()); 595 } 596 implementations.put(string, impl); 597 } 598 return implementations; 599 } 600 601 /** 602 * Assumes the class specified points to a directory in the classpath that holds files 603 * containing the name of a class that implements or is a subclass of the specfied class. 604 * <p/> 605 * Any class that cannot be loaded or are not assignable to the specified class will be 606 * skipped and placed in the 'resourcesNotLoaded' collection. 607 * <p/> 608 * Example classpath: 609 * <p/> 610 * META-INF/java.net.URLStreamHandler/jar 611 * META-INF/java.net.URLStreamHandler/file 612 * META-INF/java.net.URLStreamHandler/http 613 * <p/> 614 * ResourceFinder finder = new ResourceFinder("META-INF/"); 615 * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class); 616 * Class jarUrlHandler = map.get("jar"); 617 * Class fileUrlHandler = map.get("file"); 618 * Class httpUrlHandler = map.get("http"); 619 * 620 * @param interfase a superclass or interface 621 * @return 622 * @throws IOException if classLoader.getResources throws an exception 623 */ 624 public Map<String, Class> mapAvailableImplementations(Class interfase) throws IOException { 625 resourcesNotLoaded.clear(); 626 Map<String, Class> implementations = new HashMap<String, Class>(); 627 Map<String, String> map = mapAvailableStrings(interfase.getName()); 628 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 629 Map.Entry entry = (Map.Entry) iterator.next(); 630 String string = (String) entry.getKey(); 631 String className = (String) entry.getValue(); 632 try { 633 Class impl = classLoader.loadClass(className); 634 if (interfase.isAssignableFrom(impl)) { 635 implementations.put(string, impl); 636 } else { 637 resourcesNotLoaded.add(className); 638 } 639 } catch (Exception notAvailable) { 640 resourcesNotLoaded.add(className); 641 } 642 } 643 return implementations; 644 } 645 646 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 647 // 648 // Find Properties 649 // 650 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 651 652 /** 653 * Finds the corresponding resource and reads it in as a properties file 654 * <p/> 655 * Example classpath: 656 * <p/> 657 * META-INF/widget.properties 658 * <p/> 659 * ResourceFinder finder = new ResourceFinder("META-INF/"); 660 * Properties widgetProps = finder.findProperties("widget.properties"); 661 * 662 * @param uri 663 * @return 664 * @throws IOException if the URL cannot be read or is not in properties file format 665 */ 666 public Properties findProperties(String uri) throws IOException { 667 String fulluri = path + uri; 668 669 URL resource = getResource(fulluri); 670 if (resource == null) { 671 throw new IOException("Could not find resource: " + fulluri); 672 } 673 674 return loadProperties(resource); 675 } 676 677 /** 678 * Finds the corresponding resources and reads them in as a properties files 679 * <p/> 680 * Any URL that cannot be read in as a properties file will cause an exception to be thrown. 681 * <p/> 682 * Example classpath: 683 * <p/> 684 * META-INF/app.properties 685 * META-INF/app.properties 686 * META-INF/app.properties 687 * <p/> 688 * ResourceFinder finder = new ResourceFinder("META-INF/"); 689 * List<Properties> appProps = finder.findAllProperties("app.properties"); 690 * 691 * @param uri 692 * @return 693 * @throws IOException if the URL cannot be read or is not in properties file format 694 */ 695 public List<Properties> findAllProperties(String uri) throws IOException { 696 String fulluri = path + uri; 697 698 List<Properties> properties = new ArrayList<Properties>(); 699 700 Enumeration<URL> resources = getResources(fulluri); 701 while (resources.hasMoreElements()) { 702 URL url = resources.nextElement(); 703 Properties props = loadProperties(url); 704 properties.add(props); 705 } 706 return properties; 707 } 708 709 /** 710 * Finds the corresponding resources and reads them in as a properties files 711 * <p/> 712 * Any URL that cannot be read in as a properties file will be added to the 713 * 'resourcesNotLoaded' collection. 714 * <p/> 715 * Example classpath: 716 * <p/> 717 * META-INF/app.properties 718 * META-INF/app.properties 719 * META-INF/app.properties 720 * <p/> 721 * ResourceFinder finder = new ResourceFinder("META-INF/"); 722 * List<Properties> appProps = finder.findAvailableProperties("app.properties"); 723 * 724 * @param uri 725 * @return 726 * @throws IOException if classLoader.getResources throws an exception 727 */ 728 public List<Properties> findAvailableProperties(String uri) throws IOException { 729 resourcesNotLoaded.clear(); 730 String fulluri = path + uri; 731 732 List<Properties> properties = new ArrayList<Properties>(); 733 734 Enumeration<URL> resources = getResources(fulluri); 735 while (resources.hasMoreElements()) { 736 URL url = resources.nextElement(); 737 try { 738 Properties props = loadProperties(url); 739 properties.add(props); 740 } catch (Exception notAvailable) { 741 resourcesNotLoaded.add(url.toExternalForm()); 742 } 743 } 744 return properties; 745 } 746 747 /** 748 * Finds the corresponding resources and reads them in as a properties files 749 * <p/> 750 * Any URL that cannot be read in as a properties file will cause an exception to be thrown. 751 * <p/> 752 * Example classpath: 753 * <p/> 754 * META-INF/jdbcDrivers/oracle.properties 755 * META-INF/jdbcDrivers/mysql.props 756 * META-INF/jdbcDrivers/derby 757 * <p/> 758 * ResourceFinder finder = new ResourceFinder("META-INF/"); 759 * List<Properties> driversList = finder.findAvailableProperties("jdbcDrivers"); 760 * Properties oracleProps = driversList.get("oracle.properties"); 761 * Properties mysqlProps = driversList.get("mysql.props"); 762 * Properties derbyProps = driversList.get("derby"); 763 * 764 * @param uri 765 * @return 766 * @throws IOException if the URL cannot be read or is not in properties file format 767 */ 768 public Map<String, Properties> mapAllProperties(String uri) throws IOException { 769 Map<String, Properties> propertiesMap = new HashMap<String, Properties>(); 770 Map<String, URL> map = getResourcesMap(uri); 771 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 772 Map.Entry entry = (Map.Entry) iterator.next(); 773 String string = (String) entry.getKey(); 774 URL url = (URL) entry.getValue(); 775 Properties properties = loadProperties(url); 776 propertiesMap.put(string, properties); 777 } 778 return propertiesMap; 779 } 780 781 /** 782 * Finds the corresponding resources and reads them in as a properties files 783 * <p/> 784 * Any URL that cannot be read in as a properties file will be added to the 785 * 'resourcesNotLoaded' collection. 786 * <p/> 787 * Example classpath: 788 * <p/> 789 * META-INF/jdbcDrivers/oracle.properties 790 * META-INF/jdbcDrivers/mysql.props 791 * META-INF/jdbcDrivers/derby 792 * <p/> 793 * ResourceFinder finder = new ResourceFinder("META-INF/"); 794 * List<Properties> driversList = finder.findAvailableProperties("jdbcDrivers"); 795 * Properties oracleProps = driversList.get("oracle.properties"); 796 * Properties mysqlProps = driversList.get("mysql.props"); 797 * Properties derbyProps = driversList.get("derby"); 798 * 799 * @param uri 800 * @return 801 * @throws IOException if classLoader.getResources throws an exception 802 */ 803 public Map<String, Properties> mapAvailableProperties(String uri) throws IOException { 804 resourcesNotLoaded.clear(); 805 Map<String, Properties> propertiesMap = new HashMap<String, Properties>(); 806 Map<String, URL> map = getResourcesMap(uri); 807 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 808 Map.Entry entry = (Map.Entry) iterator.next(); 809 String string = (String) entry.getKey(); 810 URL url = (URL) entry.getValue(); 811 try { 812 Properties properties = loadProperties(url); 813 propertiesMap.put(string, properties); 814 } catch (Exception notAvailable) { 815 resourcesNotLoaded.add(url.toExternalForm()); 816 } 817 } 818 return propertiesMap; 819 } 820 821 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 822 // 823 // Map Resources 824 // 825 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 826 827 public Map<String, URL> getResourcesMap(String uri) throws IOException { 828 String basePath = path + uri; 829 830 Map<String, URL> resources = new HashMap<String, URL>(); 831 if (!basePath.endsWith("/")) { 832 basePath += "/"; 833 } 834 Enumeration<URL> urls = getResources(basePath); 835 836 while (urls.hasMoreElements()) { 837 URL location = urls.nextElement(); 838 839 try { 840 if (location.getProtocol().equals("jar")) { 841 842 readJarEntries(location, basePath, resources); 843 844 } else if (location.getProtocol().equals("file")) { 845 846 readDirectoryEntries(location, resources); 847 848 } 849 } catch (Exception e) { 850 } 851 } 852 853 return resources; 854 } 855 856 private static void readDirectoryEntries(URL location, Map<String, URL> resources) throws MalformedURLException { 857 File dir = new File(URLDecoder.decode(location.getPath())); 858 if (dir.isDirectory()) { 859 File[] files = dir.listFiles(); 860 for (File file : files) { 861 if (!file.isDirectory()) { 862 String name = file.getName(); 863 URL url = file.toURI().toURL(); 864 resources.put(name, url); 865 } 866 } 867 } 868 } 869 870 private static void readJarEntries(URL location, String basePath, Map<String, URL> resources) throws IOException { 871 JarURLConnection conn = (JarURLConnection) location.openConnection(); 872 JarFile jarfile = null; 873 jarfile = conn.getJarFile(); 874 875 Enumeration<JarEntry> entries = jarfile.entries(); 876 while (entries != null && entries.hasMoreElements()) { 877 JarEntry entry = entries.nextElement(); 878 String name = entry.getName(); 879 880 if (entry.isDirectory() || !name.startsWith(basePath) || name.length() == basePath.length()) { 881 continue; 882 } 883 884 name = name.substring(basePath.length()); 885 886 if (name.contains("/")) { 887 continue; 888 } 889 890 URL resource = new URL(location, name); 891 resources.put(name, resource); 892 } 893 } 894 895 private Properties loadProperties(URL resource) throws IOException { 896 InputStream in = resource.openStream(); 897 898 BufferedInputStream reader = null; 899 try { 900 reader = new BufferedInputStream(in); 901 Properties properties = new Properties(); 902 properties.load(reader); 903 904 return properties; 905 } finally { 906 try { 907 in.close(); 908 reader.close(); 909 } catch (Exception e) { 910 } 911 } 912 } 913 914 private String readContents(URL resource) throws IOException { 915 InputStream in = resource.openStream(); 916 BufferedInputStream reader = null; 917 StringBuffer sb = new StringBuffer(); 918 919 try { 920 reader = new BufferedInputStream(in); 921 922 int b = reader.read(); 923 while (b != -1) { 924 sb.append((char) b); 925 b = reader.read(); 926 } 927 928 return sb.toString().trim(); 929 } finally { 930 try { 931 in.close(); 932 reader.close(); 933 } catch (Exception e) { 934 } 935 } 936 } 937 938 private URL getResource(String fullUri) { 939 if (urls == null){ 940 return classLoader.getResource(fullUri); 941 } 942 return findResource(fullUri, urls); 943 } 944 945 private Enumeration<URL> getResources(String fulluri) throws IOException { 946 if (urls == null) { 947 return classLoader.getResources(fulluri); 948 } 949 Vector<URL> resources = new Vector(); 950 for (URL url : urls) { 951 URL resource = findResource(fulluri, url); 952 if (resource != null){ 953 resources.add(resource); 954 } 955 } 956 return resources.elements(); 957 } 958 959 private URL findResource(String resourceName, URL... search) { 960 for (int i = 0; i < search.length; i++) { 961 URL currentUrl = search[i]; 962 if (currentUrl == null) { 963 continue; 964 } 965 966 try { 967 String protocol = currentUrl.getProtocol(); 968 if (protocol.equals("jar")) { 969 /* 970 * If the connection for currentUrl or resURL is 971 * used, getJarFile() will throw an exception if the 972 * entry doesn't exist. 973 */ 974 URL jarURL = ((JarURLConnection) currentUrl.openConnection()).getJarFileURL(); 975 JarFile jarFile; 976 JarURLConnection juc; 977 try { 978 juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection(); 979 jarFile = juc.getJarFile(); 980 } catch (IOException e) { 981 // Don't look for this jar file again 982 search[i] = null; 983 throw e; 984 } 985 986 try { 987 juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection(); 988 jarFile = juc.getJarFile(); 989 String entryName; 990 if (currentUrl.getFile().endsWith("!/")) { 991 entryName = resourceName; 992 } else { 993 String file = currentUrl.getFile(); 994 int sepIdx = file.lastIndexOf("!/"); 995 if (sepIdx == -1) { 996 // Invalid URL, don't look here again 997 search[i] = null; 998 continue; 999 } 1000 sepIdx += 2; 1001 StringBuffer sb = new StringBuffer(file.length() - sepIdx + resourceName.length()); 1002 sb.append(file.substring(sepIdx)); 1003 sb.append(resourceName); 1004 entryName = sb.toString(); 1005 } 1006 if (entryName.equals("META-INF/") && jarFile.getEntry("META-INF/MANIFEST.MF") != null) { 1007 return targetURL(currentUrl, "META-INF/MANIFEST.MF"); 1008 } 1009 if (jarFile.getEntry(entryName) != null) { 1010 return targetURL(currentUrl, resourceName); 1011 } 1012 } finally { 1013 if (!juc.getUseCaches()) { 1014 try { 1015 jarFile.close(); 1016 } catch (Exception e) { 1017 } 1018 } 1019 } 1020 1021 } else if (protocol.equals("file")) { 1022 String baseFile = currentUrl.getFile(); 1023 String host = currentUrl.getHost(); 1024 int hostLength = 0; 1025 if (host != null) { 1026 hostLength = host.length(); 1027 } 1028 StringBuffer buf = new StringBuffer(2 + hostLength + baseFile.length() + resourceName.length()); 1029 1030 if (hostLength > 0) { 1031 buf.append("//").append(host); 1032 } 1033 // baseFile always ends with '/' 1034 buf.append(baseFile); 1035 String fixedResName = resourceName; 1036 // Do not create a UNC path, i.e. \\host 1037 while (fixedResName.startsWith("/") || fixedResName.startsWith("\\")) { 1038 fixedResName = fixedResName.substring(1); 1039 } 1040 buf.append(fixedResName); 1041 String filename = buf.toString(); 1042 File file = new File(filename); 1043 File file2 = new File(URLDecoder.decode(filename)); 1044 1045 if (file.exists() || file2.exists()) { 1046 return targetURL(currentUrl, fixedResName); 1047 } 1048 } else { 1049 URL resourceURL = targetURL(currentUrl, resourceName); 1050 URLConnection urlConnection = resourceURL.openConnection(); 1051 1052 try { 1053 urlConnection.getInputStream().close(); 1054 } catch (SecurityException e) { 1055 return null; 1056 } 1057 // HTTP can return a stream on a non-existent file 1058 // So check for the return code; 1059 if (!resourceURL.getProtocol().equals("http")) { 1060 return resourceURL; 1061 } 1062 1063 int code = ((HttpURLConnection) urlConnection).getResponseCode(); 1064 if (code >= 200 && code < 300) { 1065 return resourceURL; 1066 } 1067 } 1068 } catch (MalformedURLException e) { 1069 // Keep iterating through the URL list 1070 } catch (IOException e) { 1071 } catch (SecurityException e) { 1072 } 1073 } 1074 return null; 1075 } 1076 1077 private URL targetURL(URL base, String name) throws MalformedURLException { 1078 StringBuffer sb = new StringBuffer(base.getFile().length() + name.length()); 1079 sb.append(base.getFile()); 1080 sb.append(name); 1081 String file = sb.toString(); 1082 return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null); 1083 } 1084 }