View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  
20  package org.eclipse.jetty.webapp;
21  
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.Locale;
27  import java.util.jar.JarEntry;
28  import java.util.jar.JarInputStream;
29  import java.util.regex.Pattern;
30  
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  import org.eclipse.jetty.util.resource.Resource;
34  
35  /**
36   * JarScannerConfiguration
37   *
38   * Abstract base class for configurations that want to scan jars in
39   * WEB-INF/lib and the classloader hierarchy.
40   * 
41   * Jar name matching based on regexp patterns is provided.
42   * 
43   * Subclasses should implement the processEntry(URL jarUrl, JarEntry entry)
44   * method to handle entries in jar files whose names match the supplied 
45   * pattern.
46   */
47  public abstract class JarScanner extends org.eclipse.jetty.util.PatternMatcher
48  {
49      private static final Logger LOG = Log.getLogger(JarScanner.class);
50  
51      public abstract void processEntry (URI jarUri, JarEntry entry);
52      
53      /**
54       * Find jar names from the provided list matching a pattern.
55       * 
56       * If the pattern is null and isNullInclusive is true, then
57       * all jar names will match.
58       * 
59       * A pattern is a set of acceptable jar names. Each acceptable
60       * jar name is a regex. Each regex can be separated by either a
61       * "," or a "|". If you use a "|" this or's together the jar
62       * name patterns. This means that ordering of the matches is
63       * unimportant to you. If instead, you want to match particular
64       * jar names, and you want to match them in order, you should
65       * separate the regexs with "," instead. 
66       * 
67       * Eg "aaa-.*\\.jar|bbb-.*\\.jar"
68       * Will iterate over the jar names and match
69       * in any order.
70       * 
71       * Eg "aaa-*\\.jar,bbb-.*\\.jar"
72       * Will iterate over the jar names, matching
73       * all those starting with "aaa-" first, then "bbb-".
74       *
75       * @param pattern the pattern to use for jar matching
76       * @param uris the uris of the jars to scan
77       * @param isNullInclusive if true, an empty pattern means all names match, if false, none match
78       * @throws Exception if unable to scan
79       */
80      public void scan (Pattern pattern, URI[] uris, boolean isNullInclusive)
81      throws Exception
82      {
83         super.match(pattern, uris, isNullInclusive);
84      }
85      
86      /**
87       * Find jar names from the classloader matching a pattern.
88       * 
89       * If the pattern is null and isNullInclusive is true, then
90       * all jar names in the classloader will match.
91       * 
92       * A pattern is a set of acceptable jar names. Each acceptable
93       * jar name is a regex. Each regex can be separated by either a
94       * "," or a "|". If you use a "|" this or's together the jar
95       * name patterns. This means that ordering of the matches is
96       * unimportant to you. If instead, you want to match particular
97       * jar names, and you want to match them in order, you should
98       * separate the regexs with "," instead. 
99       * 
100      * Eg "aaa-.*\\.jar|bbb-.*\\.jar"
101      * Will iterate over the jar names in the classloader and match
102      * in any order.
103      * 
104      * Eg "aaa-*\\.jar,bbb-.*\\.jar"
105      * Will iterate over the jar names in the classloader, matching
106      * all those starting with "aaa-" first, then "bbb-".
107      * 
108      * If visitParent is true, then the pattern is applied to the
109      * parent loader hierarchy. If false, it is only applied to the
110      * classloader passed in.
111      * 
112      * @param pattern the pattern to use for jar matching
113      * @param loader the class loader to look for jars in
114      * @param isNullInclusive if true, an empty pattern means all names match, if false, none match
115      * @param visitParent if true, visit parent classloaders too
116      * @throws Exception if unable to scan
117      */
118     public void scan (Pattern pattern, ClassLoader loader, boolean isNullInclusive, boolean visitParent)
119     throws Exception
120     {
121         while (loader!=null)
122         {
123             if (loader instanceof URLClassLoader)
124             {
125                 URL[] urls = ((URLClassLoader)loader).getURLs();
126                 if (urls != null)
127                 {
128                     URI[] uris = new URI[urls.length];
129                     int i=0;
130                     for (URL u : urls)
131                         uris[i++] = u.toURI();
132                     scan (pattern, uris, isNullInclusive);
133                 }
134             }     
135             if (visitParent)
136                 loader=loader.getParent();
137             else
138                 loader = null;
139         }  
140     }
141     
142     
143     public void matched (URI uri)
144     throws Exception
145     {
146         LOG.debug("Search of {}",uri);
147         if (uri.toString().toLowerCase(Locale.ENGLISH).endsWith(".jar"))
148         {
149          
150             InputStream in = Resource.newResource(uri).getInputStream();
151             if (in==null)
152                 return;
153 
154             JarInputStream jar_in = new JarInputStream(in);
155             try
156             { 
157                 JarEntry entry = jar_in.getNextJarEntry();
158                 while (entry!=null)
159                 {
160                     processEntry(uri, entry);
161                     entry = jar_in.getNextJarEntry();
162                 }
163             }
164             finally
165             {
166                 jar_in.close();
167             }   
168         }
169     }
170 }