1   //========================================================================
2   //$Id: JarScanner.java 3028 2008-06-19 07:45:39Z janb $
3   //Copyright 2008 Mort Bay Consulting Pty. Ltd.
4   //------------------------------------------------------------------------
5   //Licensed under the Apache License, Version 2.0 (the "License");
6   //you may not use this file except in compliance with the License.
7   //You may obtain a copy of the License at 
8   //http://www.apache.org/licenses/LICENSE-2.0
9   //Unless required by applicable law or agreed to in writing, software
10  //distributed under the License is distributed on an "AS IS" BASIS,
11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //See the License for the specific language governing permissions and
13  //limitations under the License.
14  //========================================================================
15  
16  
17  package org.mortbay.jetty.webapp;
18  
19  import java.io.InputStream;
20  import java.net.URL;
21  import java.net.URLClassLoader;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.jar.JarEntry;
25  import java.util.jar.JarInputStream;
26  import java.util.regex.Pattern;
27  
28  import org.mortbay.log.Log;
29  
30  /**
31   * JarScannerConfiguration
32   *
33   * Abstract base class for configurations that want to scan jars in
34   * WEB-INF/lib and the classloader hierarchy.
35   * 
36   * Jar name matching based on regexp patterns is provided.
37   * 
38   * Subclasses should implement the processEntry(URL jarUrl, JarEntry entry)
39   * method to handle entries in jar files whose names match the supplied 
40   * pattern.
41   */
42  public abstract class JarScanner
43  {
44  
45      public abstract void processEntry (URL jarUrl, JarEntry entry);
46  
47      WebAppContext _context;
48  
49  
50      /* ------------------------------------------------------------ */
51      public void setWebAppContext(WebAppContext context)
52      {
53          _context=context;
54      }
55      
56      /* ------------------------------------------------------------ */
57      public WebAppContext getWebAppContext()
58      {
59          return _context;
60      }
61      
62      
63      /**
64       * Find jar names from the classloader matching a pattern.
65       * 
66       * If the pattern is null and isNullInclusive is true, then
67       * all jar names in the classloader will match.
68       * 
69       * A pattern is a set of acceptable jar names. Each acceptable
70       * jar name is a regex. Each regex can be separated by either a
71       * "," or a "|". If you use a "|" this or's together the jar
72       * name patterns. This means that ordering of the matches is
73       * unimportant to you. If instead, you want to match particular
74       * jar names, and you want to match them in order, you should
75       * separate the regexs with "," instead. 
76       * 
77       * Eg "aaa-.*\\.jar|bbb-.*\\.jar"
78       * Will iterate over the jar names in the classloader and match
79       * in any order.
80       * 
81       * Eg "aaa-*\\.jar,bbb-.*\\.jar"
82       * Will iterate over the jar names in the classloader, matching
83       * all those starting with "aaa-" first, then "bbb-".
84       * 
85       * If visitParent is true, then the pattern is applied to the
86       * parent loader hierarchy. If false, it is only applied to the
87       * classloader passed in.
88       * 
89       * @param pattern
90       * @param loader
91       * @param isNullInclusive
92       * @param visitParent
93       * @throws Exception
94       */
95      public void scan (Pattern pattern, ClassLoader loader, boolean isNullInclusive, boolean visitParent)
96      throws Exception
97      {
98          String[] patterns = (pattern==null?null:pattern.pattern().split(","));
99          List<Pattern> subPatterns = new ArrayList<Pattern>();
100         for (int i=0; patterns!=null && i<patterns.length;i++)
101             subPatterns.add(Pattern.compile(patterns[i]));
102         
103         
104         while (loader!=null)
105         {
106             if (loader instanceof URLClassLoader)
107             {
108                 URL[] urls = ((URLClassLoader)loader).getURLs();
109 
110                 if (urls!=null)
111                 {
112                     if (subPatterns.isEmpty())
113                     {
114                         processJars(null, urls, isNullInclusive);
115                     }
116                     else
117                     {
118                         //for each subpattern, iterate over all the urls, processing those that match
119                         for (Pattern p : subPatterns)
120                         {
121                            processJars(p, urls, isNullInclusive);
122                         }
123                     }
124                 }
125             }     
126             if (visitParent)
127                 loader=loader.getParent();
128             else
129                 loader = null;
130         }  
131     }
132     
133     
134     
135     public void processJars (Pattern pattern, URL[] urls, boolean isNullInclusive)
136     throws Exception
137     {
138         for (int i=0; i<urls.length;i++)
139         {
140             if (urls[i].toString().toLowerCase().endsWith(".jar"))
141             {
142                 String jar = urls[i].toString();
143                 int slash=jar.lastIndexOf('/');
144                 jar=jar.substring(slash+1);
145                 
146                 if ((pattern == null && isNullInclusive)
147                     ||
148                     (pattern!=null && pattern.matcher(jar).matches()))
149                 {
150                     processJar(urls[i]);
151                 }
152             }
153         }
154     }
155     
156     public void processJar (URL url)
157     throws Exception
158     {
159         Log.debug("Search of {}",url);
160 
161         InputStream in = _context.newResource(url).getInputStream();
162         if (in==null)
163             return;
164 
165         JarInputStream jar_in = new JarInputStream(in);
166         try
167         { 
168             JarEntry entry = jar_in.getNextJarEntry();
169             while (entry!=null)
170             {
171                 processEntry(url, entry);
172                 entry = jar_in.getNextJarEntry();
173             }
174         }
175         finally
176         {
177             jar_in.close();
178         }   
179     }
180 }