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 }