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