1 package org.naftulin.classpathexplorer;
2
3
4 import java.io.File;
5 import java.util.Arrays;
6 import java.util.Iterator;
7 import java.util.LinkedList;
8 import java.util.List;
9
10 import org.apache.tools.ant.BuildException;
11 import org.apache.tools.ant.Project;
12 import org.apache.tools.ant.Task;
13 import org.apache.tools.ant.types.Path;
14
15
16 /***
17 * Ant task that detects and reports dublicate resources. This task has the following
18 * properties:
19 * Path - either an inline path or path like structure that tell the task which
20 * direcotries or jar (zip) files to scan for dublicate resrources. Required
21 * attribute.
22 * AccetedExtensions - tells which files to consider when reporting dublicates.
23 * By default, all files are considered, but you might want to limit it to
24 * .class and .xml. Accepts a comma separated list of extensions. Not required.
25 * NumDublicateThreshold - how many dublicates should the task ignore, before
26 * reporing problem. Default is 0.
27 * FailOnError - if there are more dublicate files than threshold alows, should
28 * the build fail. Default is true.
29 *
30 * @author henry naftulin
31 * @version 1.0
32 */
33 public class DublicateResourceDetectorAntTask extends Task {
34 private static final String[] NO_FILTER = new String[0];
35 private final List libSet = new LinkedList();
36 private int numDublicateThreshold;
37 private boolean failOnError = true;
38 private String[] acceptedExtensions = NO_FILTER;
39
40 /***
41 * Path - either an inline path or path like structure that tell the task which
42 * direcotries or jar (zip) files to scan for dublicate resrources. Required
43 * attribute.
44 * @param p path
45 */
46 public void setPath(Path p) {
47 log("setting path " + p, Project.MSG_DEBUG);
48 libSet.add(p);
49 }
50
51 /***
52 * AccetedExtensions - tells which files to consider when reporting dublicates.
53 * By default, all files are considered, but you might want to limit it to
54 * .class and .xml. Accepts a comma separated list of extensions. Not required.
55 * @param extensions
56 */
57 public void setAcceptedExtensions(String extensions) {
58 if (extensions != null) {
59 acceptedExtensions = extensions.split(",");
60 }
61 }
62
63 /***
64 * Path - either an inline path or path like structure that tell the task which
65 * direcotries or jar (zip) files to scan for dublicate resrources. Required
66 * attribute.
67 * @return path.
68 */
69 public Path createPath() {
70 Path path = new Path(getProject());
71 libSet.add(path);
72 log("adding a path " + path, Project.MSG_DEBUG);
73 return path;
74 }
75
76 /***
77 * FailOnError - if there are more dublicate files than threshold alows, should
78 * the build fail. Default is true.
79 * @param failOnError
80 */
81 public void setFailOnError(boolean failOnError) {
82 this.failOnError = failOnError;
83 }
84
85 /***
86 * NumDublicateThreshold - how many dublicates should the task ignore, before
87 * reporing problem. Default is 0.
88 * @param numDublicateThreshold
89 */
90 public void setNumDublicateThreshold(int numDublicateThreshold) {
91 this.numDublicateThreshold = numDublicateThreshold;
92 }
93
94 /***
95 * Ant task that detects and reports dublicate resources.
96 */
97 public void execute() throws BuildException {
98 if (libSet.size() == 0) {
99 throw new BuildException("Error: you have to specify path (like classpath) to search for dublicate resources.");
100 }
101 Iterator it = libSet.iterator();
102 Path tempPath;
103 List archivesList = new LinkedList();
104 while(it.hasNext()) {
105 tempPath = (Path) it.next();
106 archivesList.addAll(Arrays.asList(tempPath.list()));
107 }
108 String[] archivesArray = new String[archivesList.size()];
109 archivesArray = (String[]) archivesList.toArray(archivesArray);
110 for(int i=0; i < archivesArray.length; ++i) {
111 File f = new File(archivesArray[i]);
112 if (!f.exists()) {
113 throw new BuildException("Error: Archive " + archivesArray[i] + " does not exist. Please remove it from the path before running this task.");
114 }
115 if (!f.isDirectory() && !archivesArray[i].endsWith(".jar") && !archivesArray[i].endsWith(".zip")) {
116 throw new BuildException("Error: Archive " + archivesArray[i] + " does not a directory or .jar or .zip. Please remove it from the path before running this task");
117 }
118 }
119 ClassPathExplorerFacade facade = new ClassPathExplorerFacade();
120 AccessibleResource[] dups = facade.getFileteredDublicateResourcesBasedOnPath(acceptedExtensions, archivesArray);
121 if (dups.length >0) {
122 if (dups.length > this.numDublicateThreshold) {
123 logDublicates(dups, failOnError ? Project.MSG_ERR : Project.MSG_WARN);
124 if (failOnError) {
125 throw new BuildException("Error: found " + dups.length + " dublicate.");
126 }
127 } else {
128 log(dups.length + " dublicates found, to see all dublicates configure ant logging to INFO level", Project.MSG_WARN);
129 logDublicates(dups, Project.MSG_INFO);
130 }
131 } else {
132 log("No dublicates found.", Project.MSG_INFO);
133 }
134 }
135
136 private void logDublicates(AccessibleResource[] dups, int logLevel) {
137 for(int i=0; i < dups.length; ++i) {
138 log("Resource dublicated: " + dups[i], logLevel);
139 }
140 }
141 }