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-15 19:02:32 -0800 (Wed, 15 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 }