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 }