1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package ch.elca.el4j.util.classpath;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.net.JarURLConnection;
23 import java.net.MalformedURLException;
24 import java.net.URISyntaxException;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.jar.JarEntry;
36 import java.util.jar.JarFile;
37
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47
48
49 public class DuplicateClassFinder {
50
51
52
53
54
55
56
57
58
59 private static final Logger s_log = LoggerFactory.getLogger("DuplicateClassFinder");
60
61
62 private Map<String, List<String>> m_classes;
63
64
65 private List<URL> m_urls;
66
67
68 private Set<String> m_duplicates;
69
70
71 private boolean m_searched = false;
72
73
74
75
76 public DuplicateClassFinder() {
77 m_classes = new HashMap<String, List<String>>();
78 m_urls = new LinkedList<URL>();
79 m_duplicates = new HashSet<String>();
80 }
81
82
83
84
85
86
87 private void assertSearched(boolean value) throws RuntimeException {
88 if (!m_searched && value) {
89 throw new RuntimeException("You must execute search() before "
90 + "performing this operation.");
91 }
92 if (m_searched && !value) {
93 throw new RuntimeException("You cannot perform this poeration "
94 + "once a search has been performed.");
95 }
96 }
97
98
99
100
101
102
103
104 private void warnDuplicate(String className, String oldLoc, String newLoc) {
105 s_log.warn("The class " + className + " is duplicated:");
106 s_log.warn("Last seen at " + oldLoc + " , duplicated in " + newLoc);
107 }
108
109
110
111
112
113 public void addUrl(URL url) {
114 assertSearched(false);
115 s_log.debug("Added URL " + url);
116 m_urls.add(url);
117 }
118
119
120
121
122
123 public void addURLClassLoader(URLClassLoader cl) {
124 for (URL url : cl.getURLs()) {
125 addUrl(url);
126 }
127 }
128
129
130
131
132
133
134 public void addClass(Class<?> c) {
135 ClassLoader cl = c.getClassLoader();
136 if (cl instanceof URLClassLoader) {
137 addURLClassLoader((URLClassLoader) cl);
138 } else {
139 addSystemClassPath();
140 }
141 }
142
143
144
145
146 public void addSystemClassPath() {
147 String cp = System.getProperty("java.class.path");
148 String[] entries = cp.split(System.getProperty("path.separator"));
149 for (String entry : entries) {
150 try {
151 addUrl(new URL(entry));
152 } catch (MalformedURLException e) {
153 throw new RuntimeException("Error adding system cp to urls.");
154 }
155 }
156 }
157
158
159
160
161 public void search() {
162 assertSearched(false);
163 m_searched = true;
164
165 s_log.info("Searching ...");
166 Iterator<URL> i = m_urls.iterator();
167
168
169
170 while (i.hasNext()) {
171 URL next = i.next();
172
173 if (!next.getProtocol().equalsIgnoreCase("FILE")) {
174 s_log.warn("The entry " + next + "is unhandled.");
175 return;
176 }
177
178 if (next.toString().endsWith(".jar")) {
179 searchJar(next);
180 } else {
181 searchDirectory(next);
182 }
183 }
184 report();
185 }
186
187
188
189
190 public void report() {
191 assertSearched(true);
192 if (m_duplicates.isEmpty()) {
193 s_log.info("The search was successful, no classes are duplicated.");
194 return;
195 }
196
197 s_log.warn("The search found " + m_duplicates.size()
198 + " duplicated classes:");
199 for (Iterator<String> i = m_duplicates.iterator(); i.hasNext();) {
200 String current = i.next();
201 List<String> theList = m_classes.get(current);
202 s_log.warn(" Class " + current + " appears "
203 + theList.size() + " times, at:");
204 for (String location : theList) {
205 s_log.warn(" " + location);
206 }
207 }
208 }
209
210
211
212
213
214
215 private void searchDirectory(URL url) {
216 s_log.info("Searching Base Directory: " + url);
217 try {
218 File baseDir = new File(url.toURI());
219 if (!baseDir.isDirectory()) {
220 s_log.warn("File " + url + " is not a directory.");
221 return;
222 }
223 recurseDirectory(baseDir.getAbsolutePath(), "");
224 } catch (URISyntaxException e) {
225 throw new RuntimeException("URL syntax error", e);
226 }
227 }
228
229
230
231
232
233
234
235 private void recurseDirectory(String base, String rel) {
236 if (!rel.equals("")) {
237 s_log.info("Recursing into: " + rel);
238 }
239 File[] files = new File(base + rel).listFiles();
240 for (File file : files) {
241 if (file.isDirectory()) {
242 recurseDirectory(base, rel + "/" + file.getName());
243 } else if (file.getName().endsWith(".class")) {
244 addClassFile(file, rel);
245 }
246 }
247 }
248
249
250
251
252
253
254 private void addClassFile(File file, String rel) {
255 String pkg = rel;
256 pkg = pkg.replaceAll("/", ".");
257 pkg = pkg.replaceAll("\\\\", ".");
258 if (pkg.startsWith(".")) {
259 pkg = pkg.substring(1);
260 }
261 String name = file.getName();
262 name = name.substring(0, name.length() - ".class".length());
263 addClass(pkg, name, file.getAbsolutePath());
264 }
265
266
267
268
269
270
271
272
273 private void addClass(String pkg, String name, String location) {
274 String className = pkg.equals("") ? name : pkg + "." + name;
275 s_log.info("Adding class " + className + " from " + location);
276 if (m_classes.get(className) != null) {
277
278
279 List<String> theList = m_classes.get(className);
280 warnDuplicate(className, theList.get(theList.size() - 1), location);
281 theList.add(location);
282 m_duplicates.add(className);
283 } else {
284
285 List <String> newList = new LinkedList<String>();
286 newList.add(location);
287 m_classes.put(className, newList);
288 }
289 }
290
291
292
293
294
295 private void searchJar(URL url) {
296 s_log.info("Searching jar: " + url);
297 try {
298 URL jar = new URL("jar:" + url.toExternalForm() + "!/");
299 JarURLConnection conn = (JarURLConnection) jar.openConnection();
300 JarFile jarFile = conn.getJarFile();
301 Enumeration<JarEntry> entries = jarFile.entries();
302 while (entries.hasMoreElements()) {
303 JarEntry e = entries.nextElement();
304 if (e.getName().endsWith(".class")) {
305 String path = e.getName();
306
307
308 String pkg
309 = path.substring(0, path.length() - ".class".length())
310 .replaceAll("/", ".");
311 if (pkg.startsWith(".")) {
312 pkg = pkg.substring(1);
313 }
314 int splitter = pkg.lastIndexOf(".");
315 String name;
316 if (splitter != -1) {
317 name = pkg.substring(splitter + 1);
318 pkg = pkg.substring(0, splitter);
319 } else {
320
321 name = pkg;
322 pkg = "";
323 }
324 addClass(pkg, name, jarFile.getName() + "!/" + path);
325 }
326 }
327
328 } catch (IOException e) {
329 throw new RuntimeException("IO Exception reading jar: " + url);
330 }
331 }
332
333
334
335
336 Iterator<String> iterator() {
337 assertSearched(true);
338 return m_classes.keySet().iterator();
339 }
340
341
342
343
344 public Set<String> getAllClasses() {
345 return new HashSet<String>(m_classes.keySet());
346 }
347
348
349
350
351
352
353 public boolean hasClass(String name) {
354 assertSearched(true);
355 return (m_classes.get(name) != null);
356 }
357
358
359
360
361
362
363 public List<String> getLocations(String name) {
364 if (!hasClass(name)) {
365 return null;
366 }
367 return m_classes.get(name);
368 }
369
370
371
372
373 public boolean duplicatesFound() {
374 assertSearched(true);
375 return (m_duplicates != null && m_duplicates.size() > 0);
376 }
377
378
379
380
381 public Set<String> getAllDuplicates() {
382 assertSearched(true);
383 return m_duplicates;
384 }
385 }