View Javadoc
1   /*
2    * #%L
3    * Nuiton Utils
4    * %%
5    * Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  
23  package org.nuiton.util;
24  
25  import org.apache.commons.io.FileUtils;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import javax.swing.ImageIcon;
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.IOException;
33  import java.net.JarURLConnection;
34  import java.net.MalformedURLException;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  import java.net.URLClassLoader;
39  import java.net.URLConnection;
40  import java.nio.file.FileSystemNotFoundException;
41  import java.nio.file.Files;
42  import java.nio.file.Path;
43  import java.nio.file.Paths;
44  import java.util.ArrayList;
45  import java.util.Collections;
46  import java.util.Enumeration;
47  import java.util.List;
48  import java.util.Objects;
49  import java.util.Optional;
50  import java.util.jar.Attributes;
51  import java.util.jar.JarEntry;
52  import java.util.jar.JarFile;
53  import java.util.jar.Manifest;
54  import java.util.stream.Stream;
55  import java.util.zip.ZipEntry;
56  import java.util.zip.ZipFile;
57  import java.util.zip.ZipInputStream;
58  
59  import static org.nuiton.i18n.I18n.t;
60  
61  /**
62   * Cette class permet de rechercher un fichier en indiquant son nom avec son
63   * chemin. Cette librairie ira ensuite chercher ce fichier sur le système de
64   * fichier, et s'il n'est pas trouvé dans le classpath. Le fichier peut donc
65   * être dans un fichier .jar ou .zip. Exemple :
66   * <pre>
67   * URL image = Resource.getURL("/images/bidulle.png");
68   * </pre>
69   *
70   * Created: 5 août 2003
71   *
72   * @author Benjamin Poussin - poussin@codelutin.com
73   *
74   */
75  public class Resource { // Resource
76  
77      /** Logger. */
78      private static final Log log = LogFactory.getLog(Resource.class);
79  
80      protected Resource() {
81  
82      }
83  
84      /**
85       * Recherche la ressource nom.
86       *
87       * @param name nom de la ressource
88       * @return l'url de la ressource
89       * @throws ResourceNotFoundException si la resource n'a pas ete trouvee
90       */
91      public static URL getURL(String name) {
92          URL url = getURLOrNull(name);
93          if (url != null) {
94              return url;
95          }
96  
97          throw new ResourceNotFoundException(t("nuitonutil.error.resource.not.found", name));
98      }
99  
100     /**
101      * Recherche la ressource nom.
102      *
103      * @param name le nom de la ressource
104      * @return l'url de la ressource ou null
105      */
106     public static URL getURLOrNull(String name) {
107         // on recherche d'abord sur le filesystem
108         File file = new File(name);
109         if (file.exists()) {
110             try {
111                 return file.toURI().toURL();
112             } catch (MalformedURLException eee) {
113                 log.warn(t("nuitonutil.error.convert.file.to.url", file, eee.getMessage()));
114             }
115         }
116 
117         // on ne l'a pas trouve on recherche dans le classpath
118 
119         // on supprime le / devant le nom de la ressource, sinon elle
120         // n'est pas trouve (pas de recherche dans les differents
121         // element du classpath.
122         if (name.length() > 1 && name.startsWith("/")) {
123             name = name.substring(1);
124         }
125         URL url = ClassLoader.getSystemClassLoader().getResource(name);
126         if (url != null) {
127             return url;
128         }
129 
130         ClassLoader cl = Resource.class.getClassLoader();
131         url = cl.getResource(name);
132         return url;
133     }
134 
135     /**
136      * Retourne l'icone demandee.
137      *
138      * @param name le nom de l'icone
139      * @return Retourne l'icon demande ou null s'il n'est pas trouvé
140      */
141     public static ImageIcon getIcon(String name) {
142         try {
143             return new ImageIcon(getURL(name));
144         } catch (Exception eee) {
145             log.warn("Can't find icon: " + name, eee);
146             return null;
147         }
148     }
149 
150     /**
151      * Retourner la liste des fichiers du classLoader. Ces fichiers doivent
152      * correspondre au pattern donne.
153      *
154      * Utile par defaut {@link ClassLoader#getSystemClassLoader()}.
155      *
156      * @param pattern le nom du fichier a extraire du fichier compressé ou
157      *                du repertoire doit correspondre au pattern (repertoire + nom
158      *                compris).
159      * @return la liste des urls correspondant au pattern
160      */
161     public static List<URL> getURLs(String pattern) {
162         return getURLs(pattern, (URLClassLoader) null);
163     }
164 
165     /**
166      * Retourner la liste des fichiers du classLoader. Ces fichiers doivent
167      * correspondre au pattern donne.
168      *
169      * @param urlClassLoader classloader to use (if null, use {@link ClassLoader#getSystemClassLoader()}
170      * @param pattern     le nom du fichier a extraire du fichier compressé ou
171      *                    du repertoire doit correspondre au pattern (repertoire + nom
172      *                    compris).
173      * @return la liste des urls correspondant au pattern
174      */
175     public static List<URL> getURLs(String pattern, URLClassLoader urlClassLoader) {
176 
177         Optional<URLClassLoader> notNullUrlClassLoader = Optional.ofNullable(urlClassLoader);
178 
179         // Si on a pas d'URLClassLoader on essaye de prendre celui du system
180         if (!notNullUrlClassLoader.isPresent()) {
181             ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
182             if (systemClassLoader instanceof URLClassLoader) {
183                 notNullUrlClassLoader = Optional.of((URLClassLoader) systemClassLoader);
184             }
185         }
186 
187         if (notNullUrlClassLoader.isPresent()) {
188             URL[] urls = ClassLoaderUtil.getURLs(notNullUrlClassLoader.get());
189             List<URL> result = getURLs(pattern, urls);
190             return result;
191         } else {
192             // Malgré les tentatives impossible de trouver un URLClassLoader, donc on va parcourir les ressources accessibles
193             List<URL> urls = walkThroughClassLoaderAndGetURLs(pattern);
194             return urls;
195         }
196     }
197 
198     private static URL safeUriToUrlOrNull(URI uri) {
199         try {
200             return uri.toURL();
201         } catch (MalformedURLException e) {
202             if (log.isWarnEnabled()) {
203                 log.warn("An error occured while walking through classpath, it may not work as expected",e);
204             }
205             return null;
206         }
207     }
208 
209     protected static List<URL> walkThroughClassLoaderAndGetURLs(String pattern) {
210 
211         try {
212             List<URL> classpathUrls = new ArrayList<>();
213 
214             Enumeration<URL> urls = ClassLoader.getSystemResources("");
215             while (urls.hasMoreElements()) {
216                 try {
217                     try (Stream<Path> walk = Files.walk(Paths.get(urls.nextElement().toURI()))) {
218 
219                         walk.filter(Files::isRegularFile)
220                                 .map(Path::toUri)
221                                 .map(Resource::safeUriToUrlOrNull)
222                                 .filter(Objects::nonNull)
223                                 .filter(url -> url.getPath().matches(pattern))
224                                 .forEach(classpathUrls::add);
225 
226                     } catch (IOException | FileSystemNotFoundException e) {
227                         if (log.isWarnEnabled()) {
228                             log.warn("An error occurred while walking through classpath, it may not work as expected", e);
229                         }
230                     }
231                 } catch (URISyntaxException urise) {
232                     if (log.isWarnEnabled()) {
233                         log.warn("An error occurred while walking through classpath, it may not work as expected", urise);
234                     }
235                 }
236             }
237 
238             return classpathUrls;
239         } catch (IOException e) {
240             return new ArrayList<>();
241         }
242     }
243 
244     /**
245      * Retourner la liste des fichiers du classLoader. Ces fichiers doivent
246      * correspondre au pattern donne.
247      *
248      * @param arrayURL les urls ou chercher
249      * @param pattern  le nom du fichier a extraire du fichier compressé ou
250      *                 dur epertoire doit correspondre au pattern (repertoire + nom
251      *                 compris).
252      * @return la liste des urls correspondant au pattern
253      */
254     public static List<URL> getURLs(String pattern, URL... arrayURL) {
255         long t0 = System.nanoTime();
256 
257         List<URL> urlList = new HashList<URL>();
258 
259         if (arrayURL.length == 1) {
260             URL jarURL = arrayURL[0];
261             if (isJar(jarURL.toString())) {
262                 // jar invocation
263                 try {
264                     arrayURL = getClassPathURLsFromJarManifest(jarURL);
265                 } catch (Exception e) {
266                     log.warn(e);
267                     arrayURL = new URL[]{jarURL};
268                 }
269             }
270         }
271         if (log.isDebugEnabled()) {
272             for (URL url : arrayURL) {
273                 log.debug("found url " + url);
274             }
275         }
276 
277         for (URL urlFile : arrayURL) {
278             // EC-20100510 this cause wrong accent encoding
279             //String fileName = urlFile.getFile();
280 //            String fileName;
281 //            try {
282 //                fileName = urlFile.toURI().getPath();
283 //            } catch (Exception e) {
284 //                if (log.isWarnEnabled()) {
285 //                    log.warn(e);
286 //                }
287 //                // warning, this can cause wrong encoding !!!
288 //                fileName = urlFile.getFile();
289 //            }
290 
291 //            // TODO deal with encoding in windows, this is very durty, but it
292 //            // works...
293             File file = FileUtils.toFile(urlFile);
294             String fileName = file.getAbsolutePath();
295 //            File file = new File(fileName.replaceAll("%20", " "));
296             if (!file.exists()) {
297                 // this case should not appear
298                 if (log.isDebugEnabled()) {
299                     log.debug("Can't find file " + file + " (" + fileName + ")");
300                 }
301                 continue;
302             }
303             if (isJar(fileName)) {
304                 // cas ou le ichier du classLoader est un fichier jar
305                 if (log.isDebugEnabled()) {
306                     log.debug("jar to search " + file);
307                 }
308                 urlList.addAll(getURLsFromJar(file, pattern));
309                 continue;
310             }
311             if (file.isDirectory()) {
312                 // cas ou le ichier du classLoader est un repertoire
313                 if (log.isDebugEnabled()) {
314                     log.debug("directory to search " + file);
315                 }
316                 // on traite le cas ou il peut y avoir des repertoire dans ce
317                 // repertoire
318                 urlList.addAll(getURLsFromDirectory(file, pattern));
319                 continue;
320             }
321 
322             if (isZip(fileName)) {
323                 // cas ou le ichier du classLoader est un fichier zip
324                 if (log.isDebugEnabled()) {
325                     log.debug("zip to search " + file);
326                 }
327                 urlList.addAll(getURLsFromZip(file, pattern));
328             }
329 
330         }
331         if (log.isInfoEnabled()) {
332             log.info("search URLs pattern: " + pattern + " in "
333                      + arrayURL.length + " urls in "
334                      + StringUtil.convertTime(System.nanoTime() - t0));
335         }
336         return urlList;
337     }
338 
339     public static URL[] getClassPathURLsFromJarManifest(URL jarURL)
340             throws IOException {
341         URL[] result;
342         File jarFile = FileUtils.toFile(jarURL);
343         if (log.isDebugEnabled()) {
344             log.debug("class-path jar to scan " + jarFile);
345         }
346         JarFile jar = new JarFile(jarFile);
347         try {
348 //            String jarPath = jarURL.toURI().getPath();
349 //            File jarFile = new File(jarPath);
350             File container = jarFile.getParentFile();
351             Manifest mf = jar.getManifest();
352             String classPath = null;
353             if (mf != null && mf.getMainAttributes() != null) {
354                 classPath = mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
355             }
356             String[] paths;
357             if (classPath != null) {
358                 paths = classPath.split(" ");
359             } else {
360                 paths = StringUtil.EMPTY_STRING_ARRAY;
361             }
362             result = new URL[paths.length + 1];
363             result[0] = jarURL;
364             File path;
365             for (int i = 0; i < paths.length; i++) {
366                 String s = paths[i];
367                 // test de l'existence d'un protocole dans le path (genre file:...)
368                 if (s.indexOf(':') != -1) {
369                     result[i + 1] = new URL(s);
370                     continue;
371                 }
372 
373                 if (s.startsWith(".") || !s.startsWith("/")) {
374                     // relative url
375                     path = new File(container, s);
376                 } else {
377                     path = new File(s);
378                 }
379                 if (log.isDebugEnabled()) {
380                     log.debug(path);
381                 }
382                 result[i + 1] = path.toURI().toURL();
383             }
384 //            jar.close();
385         } finally {
386             if (jar != null) {
387                 jar.close();
388             }
389         }
390         return result;
391     }
392 
393     public static List<URL> getURLsFromZip(File zipFile, String pattern) {
394         try {
395             if (log.isTraceEnabled()) {
396                 log.trace("search '" + pattern + "' in " + zipFile);
397             }
398 
399             List<URL> result = new ArrayList<URL>();
400 //            InputStream in = new FileInputStream(zipFile);
401             ZipInputStream zis =
402                     new ZipInputStream(new FileInputStream(zipFile));
403             try {
404                 while (zis.available() != 0) {
405                     ZipEntry entry = zis.getNextEntry();
406 
407                     if (entry == null) {
408                         break;
409                     }
410 
411                     String name = entry.getName();
412                     if (log.isTraceEnabled()) {
413                         log.trace("zipFile: " + zipFile + " name: " + name);
414                     }
415                     if (pattern == null || name.matches(pattern)) {
416                         // on recupere le fichier correspondant au pattern dans
417                         // le classloader
418                         URL url = getURL(name);
419                         // on ajoute le fichier correspondant au pattern dans
420                         // la liste
421                         if (log.isTraceEnabled()) {
422                             log.trace("zipFile: " + zipFile + " url: " + url);
423                         }
424                         result.add(url);
425                     }
426                 }
427             } finally {
428                 zis.close();
429             }
430             if (log.isTraceEnabled()) {
431                 log.trace("found with pattern '" + pattern + "' : " + result);
432             }
433             return result;
434         } catch (IOException eee) {
435             throw new ResourceException(t("nuitonutil.error.get.url.from.zip",
436                                           zipFile.getAbsolutePath(),
437                                           eee.getMessage())
438             );
439         }
440     }
441 
442     public static List<URL> getURLsFromJar(File jarfile, String pattern) {
443         try {
444             if (log.isTraceEnabled()) {
445                 log.trace("search '" + pattern + "' in " + jarfile);
446             }
447 
448             List<URL> result = new ArrayList<URL>();
449 //            InputStream in = new FileInputStream(jarfile);
450             ZipInputStream zis =
451                     new ZipInputStream(new FileInputStream(jarfile));
452             try {
453                 while (zis.available() != 0) {
454                     ZipEntry entry = zis.getNextEntry();
455 
456                     if (entry == null) {
457                         break;
458                     }
459 
460                     String name = entry.getName();
461                     if (log.isTraceEnabled()) {
462                         log.trace("jarfile: " + jarfile + " name: " + name);
463                     }
464                     if (pattern == null || name.matches(pattern)) {
465                         // on recupere le fichier correspondant au pattern
466                         // dans le classloader
467                         URL url = getURL(name);
468                         // on ajoute le fichier correspondant au pattern dans
469                         // la liste
470                         if (log.isTraceEnabled()) {
471                             log.trace("jarfile: " + jarfile + " url: " + url);
472                         }
473                         result.add(url);
474                     }
475                 }
476             } finally {
477                 zis.close();
478             }
479             if (log.isTraceEnabled()) {
480                 log.trace("found with pattern '" + pattern + "' : " + result);
481             }
482             return result;
483         } catch (IOException eee) {
484             throw new ResourceException(t("nuitonutil.error.get.url.from.zip",
485                                           jarfile.getAbsolutePath(),
486                                           eee.getMessage())
487             );
488         }
489     }
490 
491     /**
492      * Retourne la liste des fichiers correspondant au pattern donne, aucun
493      * ordre ne doit être supposé sur les fichiers.
494      *
495      * @param repository repertoire dans lequel on recherche les fichiers
496      * @param pattern    le nom du fichier a extraire du fichier du repertoire doit
497      *                   correspondre au pattern (repertoire + nom compris). si le
498      *                   pattern est null, tous les fichiers trouvé sont retourné.
499      * @return la liste des urls correspondant au pattern
500      */
501     public static List<URL> getURLsFromDirectory(File repository, String pattern) {
502         try {
503             if (log.isTraceEnabled()) {
504                 log.trace("search '" + pattern + "' in " + repository);
505             }
506 
507             List<URL> urlList = new HashList<URL>();
508             File[] filesList = repository.listFiles();
509 
510             if (filesList != null) {
511 
512                 for (File file : filesList) {
513 
514                     String name = file.getAbsolutePath();
515 
516                     if (log.isTraceEnabled()) {
517                         log.trace("directory: " + repository + " name: "
518                                   + name);
519                     }
520 
521                     // cas de recursivite : repertoire dans un repertoire
522                     if (file.exists() && file.isDirectory()) {
523                         urlList.addAll(getURLsFromDirectory(file,
524                                                             pattern));
525                         // si le fichier du repertoire n'est pas un repertoire
526                         // on verifie s'il correspond au pattern
527                     } else if (pattern == null || name.matches(pattern)) {
528                         URL url = file.toURI().toURL();
529                         if (log.isTraceEnabled()) {
530                             log.trace("directory: " + repository + " url: "
531                                       + url);
532                         }
533                         urlList.add(url);
534                     }
535                 }
536             }
537             if (log.isTraceEnabled()) {
538                 log.trace("found with pattern '" + pattern + "' : " + urlList);
539             }
540             return urlList;
541         } catch (MalformedURLException eee) {
542             throw new ResourceException(
543                     t("nuitonutil.error.convert.file.to.url",
544                       repository + " (pattern " + pattern + ") ",
545                       eee.getMessage())
546             );
547             //throw new ResourceException("Le fichier n'a pu être converti en URL", eee);
548         }
549     }
550 
551     /**
552      * Verifie si le fichier est un fichier jar.
553      *
554      * @param name nom du fichier a tester
555      * @return vrai si le fichier se termine par .jar faux sinon
556      */
557     public static boolean isJar(String name) {
558         if (name != null && name.length() > 4) {
559             String ext = name.substring(name.length() - 4, name.length());
560             return ".jar".equalsIgnoreCase(ext);
561         }
562         return false;
563     }
564 
565     /**
566      * Verifie si le fichier est un fichier zip
567      *
568      * @param name nom du fichier a tester
569      * @return vrai si le fichier se termine par .zip faux sinon
570      */
571     public static boolean isZip(String name) {
572         if (name != null && name.length() > 4) {
573             String ext = name.substring(name.length() - 4, name.length());
574             return ".zip".equalsIgnoreCase(ext);
575         }
576         return false;
577     }
578 
579     /**
580      * Test if an url contains the given directory with no recurse seeking.
581      *
582      * @param url       the url to seek
583      * @param directory the directory to find
584      * @return {@code true} if directory was found, {@code false} otherwise.
585      * @throws IOException if any io pb
586      */
587     public static boolean containsDirectDirectory(URL url, String directory) throws IOException {
588 //        String fileName = url.getFile();
589 //        // TODO deal with encoding in windows, this is very durty, but it works...
590 //        File file = new File(fileName.replaceAll("%20", " "));
591         File file = FileUtils.toFile(url);
592         String fileName = file.getAbsolutePath();
593         if (!file.exists()) {
594             return false;
595         }
596         if (isJar(fileName) || isZip(fileName)) {
597             // cas ou le fichier du classLoader est un fichier jar ou zip
598             if (log.isTraceEnabled()) {
599                 log.trace("zip to search " + file);
600             }
601             ZipFile zipFile = new ZipFile(file);
602             try {
603                 return zipFile.getEntry(directory + '/') != null;
604             } finally {
605                 zipFile.close();
606             }
607         }
608         if (file.isDirectory()) {
609             // cas ou le ichier du classLoader est un repertoire
610             if (log.isTraceEnabled()) {
611                 log.trace("directory to search " + file);
612             }
613             return new File(file, directory).exists();
614         }
615 
616         if (log.isWarnEnabled()) {
617             log.warn(t("nuitonutil.error.unknown.url.type", url));
618         }
619         return false;
620     }
621 
622     /**
623      * Return true if {@code str} is a pattern (contains * or ?).
624      *
625      * @param str str to test
626      * @return {@code true} if {@code str} is a pattern, {@code false} otherwise
627      * @since 2.2
628      */
629     protected static boolean isPattern(String str) {
630         return str.indexOf('*') != -1 || str.indexOf('?') != -1;
631     }
632 
633     /**
634      * Find pattern resouces in {@link ClassLoader#getSystemClassLoader()}.
635      *
636      * Usage :
637      * <pre>
638      *  List&lt;URL&gt; urls = Resources.getResources("META-INF/.*\\.MF");
639      *  List&lt;URL&gt; urls = Resources.getResources("org/nuiton/util/.?esource\\.class");
640      * </pre>
641      *
642      * @param pattern java regex style pattern to find
643      * @return url list found
644      * @throws IOException if any IO problem while seeking resources
645      * @since 2.2
646      */
647     public static List<URL> getResources(String pattern) throws IOException {
648         return getResources(pattern, null);
649     }
650 
651     /**
652      * Find pattern resouces in classloader.
653      *
654      * Usage :
655      * <pre>
656      *  List&lt;URL&gt; urls = Resources.getResources("META-INF/.*\\.MF");
657      *  List&lt;URL&gt; urls = Resources.getResources("org/nuiton/util/.?esource\\.class");
658      * </pre>
659      *
660      * @param pattern     java regex style pattern to find
661      * @param classLoader classLoader
662      * @return url list found
663      * @throws IOException if any IO problem while seeking resources
664      * @since 2.2
665      */
666     public static List<URL> getResources(String pattern,
667                                          ClassLoader classLoader) throws IOException {
668         if (classLoader == null) {
669             classLoader = ClassLoader.getSystemClassLoader();
670         }
671 
672         List<URL> urlList;
673 
674         if (isPattern(pattern)) {
675             urlList = getPatternRessources(pattern, classLoader);
676         } else {
677             urlList = new HashList<URL>();
678             Enumeration<URL> resourceUrls = classLoader.getResources(pattern);
679             while (resourceUrls.hasMoreElements()) {
680                 URL url = resourceUrls.nextElement();
681                 urlList.add(url);
682             }
683         }
684 
685         return urlList;
686     }
687 
688     /**
689      * Obtain some resources from a pattern using a specific class loader to
690      * seel resources.
691      *
692      * @param pattern     pattern of searched resources
693      * @param classLoader class loader which responsible to seek resources
694      * @return list of resources found
695      * @throws IOException if any IO problem while scanning resources
696      * @since 2.2
697      */
698     protected static List<URL> getPatternRessources(String pattern,
699                                                     ClassLoader classLoader) throws IOException {
700 
701         List<URL> urlList = new HashList<URL>();
702 
703         // get root directory to get URL in classpath
704         // for example :
705         // /WEB-INF/*.xml → /WEB-INF/
706         // /META-INF/persistence/*.xml → /META-INF/persistence/
707         int prefixEnd = pattern.indexOf(":") + 1;
708         int rootDirEnd = pattern.length();
709         while (rootDirEnd > prefixEnd &&
710                isPattern(pattern.substring(prefixEnd, rootDirEnd))) {
711             rootDirEnd = pattern.lastIndexOf('/', rootDirEnd - 2) + 1;
712         }
713         if (rootDirEnd == 0) {
714             rootDirEnd = prefixEnd;
715         }
716         String rootDirPath = pattern.substring(0, rootDirEnd);
717         String subPattern = pattern.substring(rootDirPath.length());
718 
719         Enumeration<URL> rootDirResources =
720                 classLoader.getResources(rootDirPath);
721 
722         while (rootDirResources.hasMoreElements()) {
723             URL rootDirResource = rootDirResources.nextElement();
724 
725             if (isJarUrl(rootDirResource)) {
726                 // cas ou le ichier du classLoader est un fichier jar
727                 if (log.isDebugEnabled()) {
728                     log.debug("jar to search " + rootDirResource);
729                 }
730                 urlList.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
731             } else {
732                 urlList.addAll(doFindMatchingFileSystemResources(rootDirResource, subPattern));
733             }
734         }
735 
736         return urlList;
737     }
738 
739     /**
740      * Test if an url detnoe a jar file.
741      *
742      * Code taken from spring source code :
743      * org.springframework.core.io.support.PathMatchingResourcePatternResolver
744      *
745      * @param url url to test
746      * @return true if url denote a jar file
747      * @since 2.2
748      */
749     public static boolean isJarUrl(URL url) {
750         String protocol = url.getProtocol();
751         return "jar".equals(protocol) ||
752                "zip".equals(protocol) ||
753                "wsjar".equals(protocol);
754     }
755 
756     /**
757      * Find all resources in jar files that match the given location pattern
758      * via the Java Regex style Matcher.
759      *
760      * Code taken from spring source code :
761      * org.springframework.core.io.support.PathMatchingResourcePatternResolver
762      *
763      * @param rootDirResource the root directory as Resource
764      * @param subPattern      the sub pattern to match (below the root directory)
765      * @return the Set of matching Resource instances
766      * @throws IOException in case of I/O errors
767      * @see JarURLConnection
768      * @since 2.2
769      */
770     protected static List<URL> doFindPathMatchingJarResources(URL rootDirResource,
771                                                               String subPattern) throws IOException {
772 
773         URLConnection con = rootDirResource.openConnection();
774         JarFile jarFile;
775         String jarFileUrl;
776         String rootEntryPath;
777         boolean newJarFile = false;
778 
779         if (con instanceof JarURLConnection) {
780             // Should usually be the case for traditional JAR files.
781             JarURLConnection jarCon = (JarURLConnection) con;
782             jarCon.setUseCaches(false);
783             jarFile = jarCon.getJarFile();
784             jarFileUrl = jarCon.getJarFileURL().toExternalForm();
785             JarEntry jarEntry = jarCon.getJarEntry();
786             rootEntryPath = jarEntry != null ? jarEntry.getName() : "";
787         } else {
788             // No JarURLConnection → need to resort to URL file parsing.
789             // We'll assume URLs of the format "jar:path!/entry", with the protocol
790             // being arbitrary as long as following the entry format.
791             // We'll also handle paths with and without leading "file:" prefix.
792             String urlFile = rootDirResource.getFile();
793             int separatorIndex = urlFile.indexOf("!/");
794             if (separatorIndex != -1) {
795                 jarFileUrl = urlFile.substring(0, separatorIndex);
796                 rootEntryPath = urlFile.substring(separatorIndex + "!/".length());
797                 //jarFile = getJarFile(jarFileUrl);
798                 if (jarFileUrl.startsWith("file:")) {
799                     jarFile = new JarFile(jarFileUrl.substring("file:".length()));
800                 } else {
801                     jarFile = new JarFile(jarFileUrl);
802                 }
803             } else {
804                 jarFile = new JarFile(urlFile);
805                 jarFileUrl = urlFile;
806                 rootEntryPath = "";
807             }
808             newJarFile = true;
809         }
810 
811         try {
812             if (log.isDebugEnabled()) {
813                 log.debug("Looking for matching resources in jar file [" + jarFileUrl + "]");
814             }
815             if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
816                 // Root entry path must end with slash to allow for proper matching.
817                 // The Sun JRE does not return a slash here, but BEA JRockit does.
818                 rootEntryPath = rootEntryPath + "/";
819             }
820             List<URL> result = new HashList<URL>(8);
821             for (Enumeration<?> entries = jarFile.entries(); entries.hasMoreElements(); ) {
822                 JarEntry entry = (JarEntry) entries.nextElement();
823                 String entryPath = entry.getName();
824                 if (entryPath.startsWith(rootEntryPath)) {
825                     String relativePath = entryPath.substring(rootEntryPath.length());
826                     if (relativePath.matches(subPattern)) {
827                         URL entryURL = new URL(rootDirResource, relativePath);
828                         result.add(entryURL);
829                     }
830                 }
831             }
832             return result;
833         } finally {
834             // Close jar file, but only if freshly obtained -
835             // not from JarURLConnection, which might cache the file reference.
836             if (newJarFile) {
837                 jarFile.close();
838             }
839         }
840     }
841 
842     /**
843      * Find all resources in the file system that match the given location pattern
844      * via the Java style matcher.
845      *
846      * Code taken from spring source code :
847      * org.springframework.core.io.support.PathMatchingResourcePatternResolver
848      *
849      * @param rootDirResource the root directory as Resource
850      * @param subPattern      the sub pattern to match (below the root directory)
851      * @return the Set of matching Resource instances
852      * @throws IOException in case of I/O errors
853      * @see #retrieveMatchingFiles(File, String)
854      * @since 2.2
855      */
856     protected static List<URL> doFindMatchingFileSystemResources(URL rootDirResource,
857                                                                  String subPattern)
858             throws IOException {
859 
860         File rootDir;
861         try {
862             rootDir = new File(rootDirResource.toURI().getSchemeSpecificPart());
863             rootDir = rootDir.getAbsoluteFile();
864         } catch (URISyntaxException ex) {
865             return Collections.emptyList();
866         }
867         return retrieveMatchingFiles(rootDir, subPattern);
868     }
869 
870     /**
871      * Retrieve files that match the given path pattern,
872      * checking the given directory and its subdirectories.
873      *
874      * Code taken from spring source code :
875      * org.springframework.core.io.support.PathMatchingResourcePatternResolver
876      *
877      * @param rootDir the directory to start from
878      * @param pattern the pattern to match against,
879      *                relative to the root directory
880      * @return the Set of matching File instances
881      * @throws IOException if directory contents could not be retrieved
882      * @since 2.2
883      */
884     protected static List<URL> retrieveMatchingFiles(File rootDir,
885                                                      String pattern) throws IOException {
886         if (!rootDir.exists()) {
887             return Collections.emptyList();
888         }
889         if (!rootDir.isDirectory()) {
890             return Collections.emptyList();
891         }
892         if (!rootDir.canRead()) {
893             return Collections.emptyList();
894         }
895         //String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
896         String fullPattern = rootDir.getAbsolutePath().replace(File.separator, "/");
897         if (!pattern.startsWith("/")) {
898             fullPattern += "/";
899         }
900         //fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
901         fullPattern = fullPattern + pattern.replace(File.separator, "/");
902         List<URL> result = new HashList<URL>(8);
903         doRetrieveMatchingFiles(fullPattern, rootDir, result);
904         return result;
905     }
906 
907     /**
908      * Recursively retrieve files that match the given pattern,
909      * adding them to the given result list.
910      *
911      * Code taken from spring source code :
912      * org.springframework.core.io.support.PathMatchingResourcePatternResolver
913      *
914      * @param fullPattern the pattern to match against,
915      *                    with preprended root directory path
916      * @param dir         the current directory
917      * @param result      the Set of matching File instances to add to
918      * @throws IOException if directory contents could not be retrieved
919      * @since 2.2
920      */
921     protected static void doRetrieveMatchingFiles(String fullPattern,
922                                                   File dir,
923                                                   List<URL> result) throws IOException {
924         if (log.isDebugEnabled()) {
925             log.debug("Searching directory [" + dir.getAbsolutePath() +
926                       "] for files matching pattern [" + fullPattern + "]");
927         }
928         File[] dirContents = dir.listFiles();
929         if (dirContents == null) {
930             if (log.isWarnEnabled()) {
931                 log.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
932             }
933             return;
934         }
935         for (File content : dirContents) {
936             //String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
937             String currPath = content.getAbsolutePath().replace(File.separator, "/");
938             //if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
939             if (content.isDirectory() && (currPath + "/").matches(fullPattern + ".*")) {
940                 if (!content.canRead()) {
941                     if (log.isDebugEnabled()) {
942                         log.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
943                                   "] because the application is not allowed to read the directory");
944                     }
945                 } else {
946                     doRetrieveMatchingFiles(fullPattern, content, result);
947                 }
948             }
949             //if (getPathMatcher().match(fullPattern, currPath)) {
950             if (currPath.matches(fullPattern)) {
951                 result.add(content.toURI().toURL());
952             }
953         }
954     }
955 
956 } // Resource