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: 634087 $ $Date: 2008-03-05 19:05:23 -0500 (Wed, 05 Mar 2008) $
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 command in : " + 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.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 JarFile jarFile = null;
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 try {
976 JarURLConnection juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection();
977 jarFile = juc.getJarFile();
978 } catch (IOException e) {
979 // Don't look for this jar file again
980 search[i] = null;
981 throw e;
982 }
983
984 String entryName;
985 if (currentUrl.getFile().endsWith("!/")) {
986 entryName = resourceName;
987 } else {
988 String file = currentUrl.getFile();
989 int sepIdx = file.lastIndexOf("!/");
990 if (sepIdx == -1) {
991 // Invalid URL, don't look here again
992 search[i] = null;
993 continue;
994 }
995 sepIdx += 2;
996 StringBuffer sb = new StringBuffer(file.length() - sepIdx + resourceName.length());
997 sb.append(file.substring(sepIdx));
998 sb.append(resourceName);
999 entryName = sb.toString();
1000 }
1001 if (entryName.equals("META-INF/") && jarFile.getEntry("META-INF/MANIFEST.MF") != null){
1002 return targetURL(currentUrl, "META-INF/MANIFEST.MF");
1003 }
1004 if (jarFile.getEntry(entryName) != null) {
1005 return targetURL(currentUrl, resourceName);
1006 }
1007 } else if (protocol.equals("file")) {
1008 String baseFile = currentUrl.getFile();
1009 String host = currentUrl.getHost();
1010 int hostLength = 0;
1011 if (host != null) {
1012 hostLength = host.length();
1013 }
1014 StringBuffer buf = new StringBuffer(2 + hostLength + baseFile.length() + resourceName.length());
1015
1016 if (hostLength > 0) {
1017 buf.append("//").append(host);
1018 }
1019 // baseFile always ends with '/'
1020 buf.append(baseFile);
1021 String fixedResName = resourceName;
1022 // Do not create a UNC path, i.e. \\host
1023 while (fixedResName.startsWith("/") || fixedResName.startsWith("\\")) {
1024 fixedResName = fixedResName.substring(1);
1025 }
1026 buf.append(fixedResName);
1027 String filename = buf.toString();
1028 File file = new File(filename);
1029 File file2 = new File(URLDecoder.decode(filename));
1030
1031 if (file.exists() || file2.exists()) {
1032 return targetURL(currentUrl, fixedResName);
1033 }
1034 } else {
1035 URL resourceURL = targetURL(currentUrl, resourceName);
1036 URLConnection urlConnection = resourceURL.openConnection();
1037
1038 try {
1039 urlConnection.getInputStream().close();
1040 } catch (SecurityException e) {
1041 return null;
1042 }
1043 // HTTP can return a stream on a non-existent file
1044 // So check for the return code;
1045 if (!resourceURL.getProtocol().equals("http")) {
1046 return resourceURL;
1047 }
1048
1049 int code = ((HttpURLConnection) urlConnection).getResponseCode();
1050 if (code >= 200 && code < 300) {
1051 return resourceURL;
1052 }
1053 }
1054 } catch (MalformedURLException e) {
1055 // Keep iterating through the URL list
1056 } catch (IOException e) {
1057 } catch (SecurityException e) {
1058 }
1059 }
1060 return null;
1061 }
1062
1063 private URL targetURL(URL base, String name) throws MalformedURLException {
1064 StringBuffer sb = new StringBuffer(base.getFile().length() + name.length());
1065 sb.append(base.getFile());
1066 sb.append(name);
1067 String file = sb.toString();
1068 return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null);
1069 }
1070 }