diff --git a/README.md b/README.md index 3ee3dd0..de6cfd6 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,8 @@ yooze ===== visualizes java class dependencies + +===== + +Not a finished project yet, just poking around with javassist. +It does build a model, and you you could pick up dot files to create pictures of the graph. It just isn't very pretty yet. diff --git a/example.png b/example.png deleted file mode 100644 index 87962ed..0000000 Binary files a/example.png and /dev/null differ diff --git a/pom.xml b/pom.xml index 02f2ab1..ce48f6a 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ jar 0.1-SNAPSHOT yooze - www.cjib.nl + http://jssl.org/yooze @@ -130,6 +130,12 @@ jackson-mapper-asl 1.9.10 + + org.mockito + mockito-core + 1.9.5 + test + diff --git a/src/main/java/yooze/ClassModelBuilder.java b/src/main/java/yooze/ClassModelBuilder.java index b25ef73..25ab100 100644 --- a/src/main/java/yooze/ClassModelBuilder.java +++ b/src/main/java/yooze/ClassModelBuilder.java @@ -21,8 +21,7 @@ import yooze.domain.MethodModel; * Builds a ClassModel. */ public class ClassModelBuilder { - private static Logger log = LoggerFactory - .getLogger(ClassModelBuilder.class); + private static Logger log = LoggerFactory.getLogger(ClassModelBuilder.class); private Pattern[] packageIncludePatterns; private Pattern[] packageExcludePatterns; @@ -47,17 +46,11 @@ public class ClassModelBuilder { private ClassModel scan(String className) { ClassModel model = new ClassModel(className); ClassCache.add(className, model); - try { - return tryScan(className, model); - } catch (Exception e) { - log.warn("Loading class,", e); - return null; - } + return tryScan(className, model); } - private ClassModel tryScan(String className, ClassModel model) - throws NotFoundException { - CtClass ctClass = pool.get(className); + private ClassModel tryScan(String className, ClassModel model) { + CtClass ctClass = getClassFromJavassist(className); if (isScannable(ctClass)) { ConstPool constPool = ctClass.getClassFile().getConstPool(); @@ -66,7 +59,15 @@ public class ClassModelBuilder { resolveMethodReferences(); return model; } else { - return null; + throw new ClassNotFound(className); + } + } + + private CtClass getClassFromJavassist(String className) { + try { + return pool.get(className); + } catch (NotFoundException e) { + throw new ClassNotFound(className); } } @@ -88,11 +89,11 @@ public class ClassModelBuilder { private void addMethods(ClassModel containingClass, CtClass ctClass) { CtMethod[] methods = ctClass.getMethods(); for (CtMethod method : methods) { - containingClass.addMethod(MethodModel.create(containingClass, - method)); + containingClass.addMethod(MethodModel.create(containingClass, method)); } } + @SuppressWarnings("unchecked") private void addClassReferences(ClassModel model, ConstPool constPool) { Set classNames = constPool.getClassNames(); for (String classResourcename : classNames) { diff --git a/src/main/java/yooze/ClassesDirScanner.java b/src/main/java/yooze/ClassesDirScanner.java deleted file mode 100644 index 724cddd..0000000 --- a/src/main/java/yooze/ClassesDirScanner.java +++ /dev/null @@ -1,28 +0,0 @@ -package yooze; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javassist.ClassPath; -import yooze.scanner.Scanner; - -/** - * reads classes as .class files from a directory - */ -public class ClassesDirScanner implements Scanner { - - @Override - public List scanArchive(String archiveName) throws IOException { - return scanArchive(new File(archiveName)); - } - - @Override - public List scanArchive(File file) throws IOException { - List result = new ArrayList(); - result.add(new DirClassPath(file)); - return result; - } - -} diff --git a/src/main/java/yooze/DirClassPath.java b/src/main/java/yooze/DirClassPath.java deleted file mode 100644 index bc1c88c..0000000 --- a/src/main/java/yooze/DirClassPath.java +++ /dev/null @@ -1,90 +0,0 @@ -package yooze; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import javassist.ClassPath; -import javassist.NotFoundException; - -public class DirClassPath implements ClassPath, Inspectable { - - private final File dir; - - public DirClassPath(File dir) { - super(); - this.dir = dir; - } - - public void close() { - } - - public URL find(String className) { - try { - return new URL("file:///" + dir.getCanonicalPath() + "/" + Util.toClassResource(className)); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return "DirectoryClasspath[" + dir + "]"; - } - - public List getClasses() { - List classes = new ArrayList(); - getClasses(dir, dir, classes); - return classes; - } - - private void getClasses(File root, File dir, List classes) { - File[] fileList = dir.listFiles(); - for (File entry : fileList) { - if (entry.isDirectory()) { - // recurse deeper - getClasses(root, entry, classes); - } else if (isClassFile(entry)) { - classes.add(createQualifiedClassNameFromFileLocation(root, entry)); - } - } - } - - private boolean isClassFile(File entry) { - return entry.isFile() && entry.getName().endsWith(".class"); - } - - private String createQualifiedClassNameFromFileLocation(File root, File classFile) { - String absolutePath = classFile.getAbsolutePath(); - String relativePath = absolutePath.substring(root.getAbsolutePath().length() + 1); - String packageFormat = relativePath.replaceAll("\\\\", ".").replaceAll("/", "."); - String substring = packageFormat.substring(0, packageFormat.length() - 6); - return substring; - } - - @Override - public String getResourceName() { - return dir.getName(); - } - - @Override - public InputStream openClassfile(String className) throws NotFoundException { - File classFile = new File(dir, Util.toClassResource(className) + ".class"); - if (!classFile.exists()) { - return null; - } - try { - return new ClassByteCountingInputStream(className, new FileInputStream(classFile)); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/yooze/DotPrinter.java b/src/main/java/yooze/DotPrinter.java deleted file mode 100644 index a0f809b..0000000 --- a/src/main/java/yooze/DotPrinter.java +++ /dev/null @@ -1,59 +0,0 @@ -package yooze; - -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.ArrayList; - -import yooze.domain.ClassModel; -import yooze.domain.Graph; - -/** - * prints the graph as a graphviz dot file. Takes care of circular dependencies. Not threadsafe. - */ -public class DotPrinter extends PrintStream { - - private ArrayList printedRelations; - - public DotPrinter(OutputStream out) { - super(out); - } - - public void print(Graph g) { - printedRelations = new ArrayList(); - println("digraph \"" + g.getName() + "\" {"); - println("graph [size=100,100];"); - for (ClassModel cm : g.getChildren()) { - print(cm); - } - println("}"); - close(); - } - - private void print(ClassModel cm) { - double boxsize = Math.sqrt(Statistics.getByteCodeSizeForClass(cm.getName())); - - print("\""); - print(cm.getName()); - println("\" [shape=box, height=" + boxsize / 20 + "];"); - - if (cm.getReferences() == null || cm.getReferences().size() == 0) { - print("\""); - print(cm.getName()); - println("\";"); - } else { - for (ClassModel ref : cm.getReferences()) { - String relation = cm.getName() + "-" + ref.getName(); - if (!printedRelations.contains(relation)) { - print("\""); - print(cm.getName()); - print("\" -> \""); - print(ref.getName()); - println("\""); - printedRelations.add(relation); - print(ref); - } - } - } - } - -} diff --git a/src/main/java/yooze/GraphBuilder.java b/src/main/java/yooze/GraphBuilder.java index 48b067a..b5b2e4f 100644 --- a/src/main/java/yooze/GraphBuilder.java +++ b/src/main/java/yooze/GraphBuilder.java @@ -8,14 +8,9 @@ import javassist.ClassPath; import javassist.ClassPool; import yooze.domain.ClassModel; import yooze.domain.Graph; -import yooze.scanner.ArchiveScanner; -import yooze.scanner.LibScanner; -import yooze.scanner.Scanner; -import yooze.scanner.TgzScanner; /** - * Builds a class dependency graph from given classpath. Delegates to - * ClassModelBuilder. + * Builds a class dependency graph from given classpath. Delegates to ClassModelBuilder. */ public class GraphBuilder { private Scanner scanner; @@ -23,83 +18,67 @@ public class GraphBuilder { private String[] packageIncludePatterns; private String[] packageExcludePatterns; - /** - * Factory method for getting a builder that does earfiles - */ - public static GraphBuilder getEarBuilder() { - return new GraphBuilder(new ArchiveScanner()); - } - - /** - * Factory method for getting a builder that does (downloaded) .tar.gz files - */ - public static GraphBuilder getDefaultTgzBuilder() { - GraphBuilder tgzBuilder = new GraphBuilder(new TgzScanner()); - tgzBuilder.setPackageExcludePatterns("java.*", "sun.*", "com.sun.*"); - return tgzBuilder; - } - - /** - * Factory method for getting a builder that scans a lib directory - * (containing jars) - */ - public static GraphBuilder getLibDirectoryBuilder() { - return new GraphBuilder(new LibScanner()); - } - - /** - * Factory method for getting a builder that scans a directory containing - * classes - */ - public static GraphBuilder getClassesDirectoryBuilder() { - return new GraphBuilder(new ClassesDirScanner()); - } - - private GraphBuilder(Scanner scanner) { + public GraphBuilder(Scanner scanner) { this.scanner = scanner; } /** - * primary function for this class. + * Builds a graph from a give starting point(class) + * + * @param archiveFilename + * @param className + * the name of the class that is the starting point. Only classes referenced from here will be included. + * @return a Graph containing all calculated dependencies + * @throws IOException + * when file reading fails + */ + public Graph build(String archiveFilename, String className) throws IOException { + return buildClassDepencyGraph(new File(archiveFilename), className, new IncludeDecision() { + public boolean shouldIncludeClass(String name, String startingpointname) { + return name.equals(startingpointname); + } + }); + } + + /** + * Builds a graph for all classes (all included via package patterns and not excluded) * * @param archiveFilename * @return a Graph containing all calculated dependencies * @throws IOException * when file reading fails */ - public Graph build(String archiveFilename, String className) - throws IOException { - return buildClassDepencyGraph(new File(archiveFilename), className); + public Graph build(String archiveFilename) throws IOException { + return buildClassDepencyGraph(new File(archiveFilename), null, new IncludeDecision() { + public boolean shouldIncludeClass(String name, String startingpointname) { + return true; + } + }); } - Graph buildClassDepencyGraph(File archiveFile, String className) - throws IOException { - List cpList = scanner.scanArchive(archiveFile); + Graph buildClassDepencyGraph(File archiveFile, String className, IncludeDecision e) throws IOException { + List cpList = scanner.scanArchive(archiveFile); ClassPool pool = ClassPool.getDefault(); for (ClassPath cp : cpList) { pool.appendClassPath(cp); } - Graph graph = createClassDependencyGraph(pool, cpList, className); + Graph graph = createClassDependencyGraph(pool, cpList, className, e); graph.setName(archiveFile.getName()); return graph; } - private Graph createClassDependencyGraph(ClassPool pool, - List classpath, String className) { + private Graph createClassDependencyGraph(ClassPool pool, List classpath, String className, + IncludeDecision decide) { Graph graph = new Graph(); classModelBuilder = new ClassModelBuilder(pool); classModelBuilder.setPackageExcludePatterns(packageExcludePatterns); classModelBuilder.setPackageIncludePatterns(packageIncludePatterns); - for (ClassPath lib : classpath) { - assert (lib instanceof Inspectable); - - List classes = ((Inspectable) lib).getClasses(); - for (String name : classes) { - if (name.equals(className)) { - ClassModel newModel = classModelBuilder - .scanClassOrSkip(className); + for (InspectableClasspath lib : classpath) { + for (String name : lib.getClasses()) { + if (decide.shouldIncludeClass(name, className)) { + ClassModel newModel = classModelBuilder.scanClassOrSkip(className); if (newModel != null) { graph.add(newModel); } @@ -117,4 +96,7 @@ public class GraphBuilder { this.packageExcludePatterns = packageExcludePatterns; } + interface IncludeDecision { + boolean shouldIncludeClass(String name, String startingpointname); + } } diff --git a/src/main/java/yooze/Inspectable.java b/src/main/java/yooze/Inspectable.java deleted file mode 100644 index 07bb261..0000000 --- a/src/main/java/yooze/Inspectable.java +++ /dev/null @@ -1,8 +0,0 @@ -package yooze; - -import java.util.List; - -public interface Inspectable { - public String getResourceName(); - public List getClasses(); -} diff --git a/src/main/java/yooze/JarClassPath.java b/src/main/java/yooze/JarClassPath.java deleted file mode 100644 index 27712d6..0000000 --- a/src/main/java/yooze/JarClassPath.java +++ /dev/null @@ -1,78 +0,0 @@ -package yooze; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; - -import javassist.ClassPath; -import javassist.NotFoundException; - -public class JarClassPath implements ClassPath, Inspectable { - - private final JarFile jar; - - public JarClassPath(JarFile jar) { - super(); - this.jar = jar; - } - - public void close() { - try { - jar.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public URL find(String className) { - try { - return new URL("file:///"+jar.getName()+"!"+Util.toClassResource(className)); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - - - @Override - public String toString() { - return "JarClasspath[" + jar.getName() + "]"; - } - - public List getClasses() { - List classes = new ArrayList(); - for (Enumeration entries = jar.entries(); entries - .hasMoreElements();) { - String name = entries.nextElement().getName().replaceAll("/", "."); - if (name.endsWith(".class")) { - classes.add(name.substring(0, name.length() - 6)); - } - } - return classes; - } - - - public String getResourceName() { - return jar.getName(); - } - - @Override - public InputStream openClassfile(String className) throws NotFoundException { - ZipEntry entry = jar.getEntry(Util.toClassResource(className)); - if (entry == null) { - return null; - } - try { - return new ClassByteCountingInputStream(className,jar.getInputStream(entry)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/yooze/domain/MethodCallModel.java b/src/main/java/yooze/domain/MethodCallModel.java index 5c1fccd..5887c42 100644 --- a/src/main/java/yooze/domain/MethodCallModel.java +++ b/src/main/java/yooze/domain/MethodCallModel.java @@ -19,10 +19,9 @@ public class MethodCallModel { public MethodModel getCalledMethod() { try { ParameterList parameterList = ParameterList.create(methodCall.getMethod()); - return MethodCache.getInstance().get( - createQualifiedMethodname(parameterList)); + return MethodCache.getInstance().get(createQualifiedMethodname(parameterList)); } catch (NotFoundException e) { - throw new RuntimeException(e); + throw new MethodNotFound(e); } } diff --git a/src/main/java/yooze/etc/ClassBuilder.java b/src/main/java/yooze/etc/ClassBuilder.java deleted file mode 100644 index 2751b28..0000000 --- a/src/main/java/yooze/etc/ClassBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -package yooze.etc; - -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -import yooze.Util; -import yooze.domain.ClassModel; - -import javassist.ClassPath; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.ConstPool; - -/** - * @deprecated does not seem to be in use - * - */ -public class ClassBuilder { - - private final ConcurrentHashMap classes = new ConcurrentHashMap(); - - private ClassPool pool = ClassPool.getDefault(); - - private ClassBuilder(List classpaths) { - super(); - for (ClassPath cpEntry : classpaths) { - pool.appendClassPath(cpEntry); - } - } - - public ClassModel load(String className) throws NotFoundException { - ClassModel model; - if ((model = classes.get(className)) != null) { - return model; - } - - CtClass ctClass = pool.getCtClass(className); - model = new ClassModel(className); - ConstPool constPool = ctClass.getClassFile().getConstPool(); - for (Iterator classNames = constPool.getClassNames().iterator(); classNames.hasNext();) { - model.addReference(load(Util.toClassName(classNames.next().toString()))); - } - return model; - - } -} diff --git a/src/main/java/yooze/etc/Neo4jDao.java b/src/main/java/yooze/etc/Neo4jDao.java deleted file mode 100644 index ad08c2d..0000000 --- a/src/main/java/yooze/etc/Neo4jDao.java +++ /dev/null @@ -1,89 +0,0 @@ -package yooze.etc; - -import java.util.concurrent.ConcurrentHashMap; - -import org.neo4j.graphdb.Direction; -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.RelationshipType; -import org.neo4j.graphdb.ReturnableEvaluator; -import org.neo4j.graphdb.StopEvaluator; -import org.neo4j.graphdb.Transaction; -import org.neo4j.graphdb.Traverser; -import org.neo4j.kernel.EmbeddedGraphDatabase; - -import yooze.domain.ClassModel; - -public class Neo4jDao { - private GraphDatabaseService graphDb; - private Node rootNode; - private ConcurrentHashMap nodes = new ConcurrentHashMap(); - - public enum MyRelationshipTypes implements RelationshipType { - CONTAINS, REFERENCES - } - - public Neo4jDao(String database) { - graphDb = new EmbeddedGraphDatabase(database); - Transaction tx = graphDb.beginTx(); - try { - rootNode = graphDb.createNode(); - rootNode.setProperty("name", "root"); - tx.success(); - } finally { - tx.finish(); - } - } - - public void insertClass(ClassModel classModel) { - Transaction tx = graphDb.beginTx(); - try { - Node newNode = findOrCreateNode(classModel); - - for (ClassModel ref : classModel.getReferences()) { - Node refNode = findOrCreateNode(ref); - newNode.createRelationshipTo(refNode, - MyRelationshipTypes.REFERENCES); - } - tx.success(); - } finally { - tx.finish(); - } - } - - private Node findOrCreateNode(ClassModel classModel) { - Node newNode = findNode(classModel.getName()); - if (newNode == null) { - Transaction tx = graphDb.beginTx(); - try { - newNode = graphDb.createNode(); - newNode.setProperty("name", classModel.getName()); - - tx.success(); - } finally { - tx.finish(); - } - } - return newNode; - } - - public Node findNode(String className) { - Node node = nodes.get(className); - if (node != null) { - return node; - } - // do "tablescan". is there a better way? - Traverser classesTraverser = rootNode.traverse( - Traverser.Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE, - ReturnableEvaluator.ALL_BUT_START_NODE, - MyRelationshipTypes.CONTAINS, Direction.OUTGOING); - for (Node nextNode : classesTraverser) { - if (nextNode.getProperty("name").equals(className)) { - nodes.put(className, nextNode); - return nextNode; - } - } - return null;// not found - - } -} diff --git a/src/main/java/yooze/etc/Yooze.java b/src/main/java/yooze/etc/Yooze.java deleted file mode 100644 index b6c0ce9..0000000 --- a/src/main/java/yooze/etc/Yooze.java +++ /dev/null @@ -1,43 +0,0 @@ -package yooze.etc; - -import java.io.IOException; - -import yooze.GraphBuilder; -import yooze.domain.ClassModel; -import yooze.domain.Graph; - -public class Yooze { - private String neo4jDb; - - public static void main(String[] args) throws IOException { - String neoDb = args[0]; - String earfile = args[1]; - String in = args[2]; - String ex = args[2]; - String startingClassname = args[3]; - new Yooze(neoDb).createNeoGraph(earfile, in, ex, startingClassname); - - } - - public Yooze(String neo4jDb) { - super(); - this.neo4jDb = neo4jDb; - } - - public void createNeoGraph(String archive, String packageIncludePatterns, - String packageExcludePatterns, String startingClass) - throws IOException { - GraphBuilder libDirectoryBuilder = GraphBuilder - .getLibDirectoryBuilder(); - libDirectoryBuilder.setPackageExcludePatterns(packageExcludePatterns); - libDirectoryBuilder.setPackageIncludePatterns(packageIncludePatterns); - - Graph graph = libDirectoryBuilder.build(archive, startingClass); - - Neo4jDao neo4jDao = new Neo4jDao(neo4jDb); - for (ClassModel model : graph.getChildren()) { - neo4jDao.insertClass(model); - } - } - -} diff --git a/src/main/java/yooze/scanner/ArchiveScanner.java b/src/main/java/yooze/scanner/ArchiveScanner.java index 6f14aa4..db300aa 100644 --- a/src/main/java/yooze/scanner/ArchiveScanner.java +++ b/src/main/java/yooze/scanner/ArchiveScanner.java @@ -1,7 +1,6 @@ package yooze.scanner; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -9,147 +8,79 @@ import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.ZipEntry; -import javassist.ClassPath; - -import org.apache.commons.io.IOUtils; - -import yooze.DirClassPath; -import yooze.JarClassPath; +import yooze.InspectableClasspath; +import yooze.Scanner; import yooze.Util; /** - * @author shautvast TODO separate models for wars + * TODO separate models for wars */ public class ArchiveScanner implements Scanner { - private final static Pattern classPattern = Pattern.compile("WEB-INF/classes/(.*)\\.class"); - private final static Pattern packagePattern = Pattern.compile("(.+\\/+)(.+\\.class)"); + private WarScanner warScanner; - public List scanArchive(String archiveName) throws IOException { + public ArchiveScanner(WarScanner warScanner) { + this.warScanner = warScanner; + } + + public List scanArchive(String archiveName) { return scanArchive(new File(archiveName)); } - public List scanArchive(File archiveFile) throws IOException { - List result = new ArrayList(); + public List scanArchive(File archiveFile) { + List result = new ArrayList(); if (!archiveFile.exists()) { return result; } if (archiveFile.isDirectory()) { - File[] list = archiveFile.listFiles(); - for (File entry : list) { - if (entry.getName().endsWith(".jar")) { - result.add(new JarClassPath(new JarFile(entry))); - } else if (entry.getName().endsWith(".class")) { - result.add(new DirClassPath(archiveFile)); - break; - } - } + return directoryAsClasspath(archiveFile, result); + } else { + JarFile archive = createJar(archiveFile); + if (isEarFile(archive)) { + result.addAll(scanRoot(archive)); + result.addAll(warScanner.scanWars(getWars(archive))); + } else if (isWarFile(archive)) { + result.addAll(warScanner.scanWars(Arrays.asList(new JarFile[] { archive }))); + } else { + // treat as jar file + result.add(new JarClassPath(archive)); + } return result; } + } - JarFile archive = new JarFile(archiveFile); - if (archive.getName().endsWith(".ear")) { - result.addAll(scanRoot(archive)); - result.addAll(scanWars(getWars(archive))); + private boolean isWarFile(JarFile archive) { + return archive.getName().endsWith(".war"); + } - } else if (archive.getName().endsWith(".war")) { - result.addAll(scanWars(Arrays.asList(new JarFile[] { archive }))); - } else { - // treat as jar file - result.add(new JarClassPath(archive)); + private boolean isEarFile(JarFile archive) { + return archive.getName().endsWith(".ear"); + } + + private List directoryAsClasspath(File archiveFile, List result) { + File[] list = archiveFile.listFiles(); + for (File entry : list) { + if (entry.getName().endsWith(".jar")) { + result.add(new JarClassPath(createJar(entry))); + } else if (entry.getName().endsWith(".class")) { + result.add(new DirClassPath(archiveFile)); + break; + } } + return result; } - private List scanWars(List wars) { - List classpaths = new ArrayList(); - for (JarFile war : wars) { - classpaths.addAll(scanWar(war)); - } - return classpaths; - } - - private List scanWar(JarFile warfile) { - List classpaths = new ArrayList(); - File classesDir = createTempLocation(classpaths); - classpaths.add(new DirClassPath(classesDir)); - for (Enumeration entries = warfile.entries(); entries.hasMoreElements();) { - JarEntry entry = (JarEntry) entries.nextElement(); - if (isArchive(entry)) { - try { - addToClasspath(warfile, classpaths, entry); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else { - extractClass(warfile, entry, classesDir); - } - } - return classpaths; - } - - private File createTempLocation(List classpaths) { - File classesDir = new File(new File(System.getProperty("java.io.tmpdir")), "classes" - + System.currentTimeMillis()); - boolean dirsMade = classesDir.mkdirs(); - if (!dirsMade) { - throw new RuntimeException("Directory " + classesDir + " could not be created"); - } - return classesDir; - } - - private void addToClasspath(JarFile warfile, List classpaths, JarEntry entry) throws IOException { - File jarFile = Util.extractFile(warfile, entry); - classpaths.add(new JarClassPath(new JarFile(jarFile))); - } - - private boolean isArchive(JarEntry entry) { - String name = entry.getName(); - return name.startsWith("WEB-INF/lib") && name.endsWith(".jar"); - } - - private File extractClass(JarFile warfile, ZipEntry entry, File classesDir) { - Matcher matcher = classPattern.matcher(entry.getName()); - if (matcher.matches()) { - String className = matcher.group(1) + ".class"; - Matcher matcher2 = packagePattern.matcher(className); - if (matcher2.matches()) { - String packageName = matcher2.group(1); - File classDir = createUnarchivedPackageDicectory(classesDir, packageName); - String simpleClassName = matcher2.group(2); - File classFile = new File(classDir, simpleClassName); - return createUnarchivedClassfile(warfile, entry, classFile); - } - } - return null; - } - - private File createUnarchivedClassfile(JarFile warfile, ZipEntry entry, File classFile) { + private JarFile createJar(File archiveFile) { try { - FileOutputStream out = new FileOutputStream(classFile); - IOUtils.copy(warfile.getInputStream(entry), out); - return classFile; + return new JarFile(archiveFile); } catch (IOException e) { - throw new RuntimeException(e); + throw new JarCouldNotBeCreated(archiveFile.getAbsolutePath()); } } - private File createUnarchivedPackageDicectory(File classesDir, String packageName) { - File classDir = new File(classesDir, packageName); - if (!classDir.exists()) { - boolean packageDirsMade = classDir.mkdirs(); - if (!packageDirsMade) { - throw new RuntimeException("Directory " + classDir + " could not be created"); - } - } - return classDir; - } - private List getWars(JarFile earfile) { List wars = new ArrayList(); for (Enumeration entries = earfile.entries(); entries.hasMoreElements();) { @@ -170,13 +101,13 @@ public class ArchiveScanner implements Scanner { return entry.getName().endsWith(".war"); } - private List scanRoot(JarFile earfile) { - List classpaths = new ArrayList(); + private List scanRoot(JarFile earfile) { + List classpaths = new ArrayList(); for (Enumeration entries = earfile.entries(); entries.hasMoreElements();) { JarEntry entry = (JarEntry) entries.nextElement(); if (isJarfile(entry)) { try { - addToClasspath(earfile, classpaths, entry); + ClasspathAdder.addEntriesFromWarToClasspath(earfile, classpaths, entry); } catch (IOException e) { throw new RuntimeException(e); } @@ -189,4 +120,10 @@ public class ArchiveScanner implements Scanner { return entry.getName().endsWith(".jar"); } + @SuppressWarnings("serial") + private static class JarCouldNotBeCreated extends RuntimeException { + public JarCouldNotBeCreated(String name) { + super(name); + } + } } diff --git a/src/main/java/yooze/scanner/LibScanner.java b/src/main/java/yooze/scanner/LibScanner.java index bd0747d..68b1095 100644 --- a/src/main/java/yooze/scanner/LibScanner.java +++ b/src/main/java/yooze/scanner/LibScanner.java @@ -6,33 +6,30 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.JarFile; -import yooze.JarClassPath; - -import javassist.ClassPath; +import yooze.InspectableClasspath; +import yooze.Scanner; /** - * Scans a directory ("lib") recursively for jarfiles and adds them to the - * classpath - * - * @author sander - * + * Scans a directory ("lib") recursively for jarfiles and adds them to the classpath */ public class LibScanner implements Scanner { @Override - public List scanArchive(String archiveName) throws IOException { + public List scanArchive(String archiveName) throws IOException { return scanArchive(new File(archiveName)); } @Override - public List scanArchive(File file) throws IOException { - List classpaths = new ArrayList(); - return doScanArchive(classpaths,file); + public List scanArchive(File file) throws IOException { + List classpaths = new ArrayList(); + return doScanArchive(classpaths, file); } - private List doScanArchive(List classpaths, File file) + /* + * return argument classpaths for sake of recursion + */ + private List doScanArchive(List classpaths, File file) throws IOException { - File[] entries = file.listFiles(); for (File entry : entries) { if (entry.isDirectory()) { @@ -43,5 +40,4 @@ public class LibScanner implements Scanner { } return classpaths; } - } diff --git a/src/main/java/yooze/scanner/Scanner.java b/src/main/java/yooze/scanner/Scanner.java deleted file mode 100644 index d7883ae..0000000 --- a/src/main/java/yooze/scanner/Scanner.java +++ /dev/null @@ -1,13 +0,0 @@ -package yooze.scanner; - -import java.io.File; -import java.io.IOException; -import java.util.List; - -import javassist.ClassPath; - -public interface Scanner { - public List scanArchive(String archiveName) throws IOException; - - public List scanArchive(File file) throws IOException; -} diff --git a/src/main/java/yooze/scanner/TgzScanner.java b/src/main/java/yooze/scanner/TgzScanner.java index bb9302d..3f8fd5d 100644 --- a/src/main/java/yooze/scanner/TgzScanner.java +++ b/src/main/java/yooze/scanner/TgzScanner.java @@ -11,34 +11,32 @@ import java.util.List; import java.util.jar.JarFile; import java.util.zip.GZIPInputStream; -import javassist.ClassPath; - import org.xeustechnologies.jtar.TarEntry; import org.xeustechnologies.jtar.TarInputStream; -import yooze.JarClassPath; +import yooze.InspectableClasspath; +import yooze.Scanner; /** * @author sander * */ public class TgzScanner implements Scanner { - public List scanArchive(String archiveName) throws IOException { + public List scanArchive(String archiveName) throws IOException { return scanArchive(new File(archiveName)); } - public List scanArchive(File file) throws IOException { - List classpaths = new ArrayList(); - TarInputStream tarInputStream = new TarInputStream(new GZIPInputStream( - new BufferedInputStream(new FileInputStream(file)))); + public List scanArchive(File file) throws IOException { + List classpaths = new ArrayList(); + TarInputStream tarInputStream = new TarInputStream(new GZIPInputStream(new BufferedInputStream( + new FileInputStream(file)))); TarEntry entry; while ((entry = tarInputStream.getNextEntry()) != null) { if (entry.getName().endsWith(".jar")) { int count; byte data[] = new byte[2048]; - File tempFile = File.createTempFile( - singleName(entry.getName()), ".jar"); + File tempFile = File.createTempFile(singleName(entry.getName()), ".jar"); FileOutputStream fos = new FileOutputStream(tempFile); BufferedOutputStream dest = new BufferedOutputStream(fos); diff --git a/src/test/java/yooze/AssTest.java b/src/test/java/yooze/AssTest.java deleted file mode 100644 index 965b95b..0000000 --- a/src/test/java/yooze/AssTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package yooze; - -import java.util.Iterator; - -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.ConstPool; - -public class AssTest { - public static void main(String[] args) throws NotFoundException { - ClassPool pool = ClassPool.getDefault(); - CtClass c1 = pool.get("yooze.Class1"); - ConstPool constPool = c1.getClassFile().getConstPool(); - - - for (Iterator iterator = constPool.getClassNames().iterator();iterator.hasNext();){ - System.out.println(iterator.next()); - } - - } -} diff --git a/src/test/java/yooze/DotPrinterTest.java b/src/test/java/yooze/DotPrinterTest.java deleted file mode 100644 index ff3aac6..0000000 --- a/src/test/java/yooze/DotPrinterTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package yooze; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import junit.framework.Assert; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import yooze.domain.Graph; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "classpath:applicationContext-test.xml") -public class DotPrinterTest { - - @Before - public void clearClassCache() { - ClassCache.clear(); - } - - @Test - public void dotPrinting() throws IOException { - GraphBuilder directoryBuilder = GraphBuilder - .getClassesDirectoryBuilder(); - directoryBuilder.setPackageExcludePatterns(".*?Class4"); - directoryBuilder.setPackageIncludePatterns(".*?.Class."); - Graph graph = directoryBuilder.build("target/test-classes", - "yooze.Class1"); - - ByteArrayOutputStream bytes = new ByteArrayOutputStream(500); - DotPrinter d = new DotPrinter(bytes); - d.print(graph); - String dotText = new String(bytes.toByteArray()); - d.close(); - String[] expectedDotTextLines = { "digraph \"test-classes\" {", - "graph [size=100,100];", - "\"yooze.Class1\" [shape=box, height=0.0];", - "\"yooze.Class1\" -> \"yooze.Class2\"", - "\"yooze.Class2\" [shape=box, height=0.0];", - "\"yooze.Class2\" -> \"yooze.Class3\"", - "\"yooze.Class3\" [shape=box, height=0.0];", - "\"yooze.Class3\" -> \"yooze.Class1\"", - "\"yooze.Class1\" [shape=box, height=0.0];", - "\"yooze.Class2\" [shape=box, height=0.0];", - "\"yooze.Class3\" [shape=box, height=0.0];", "}" }; - expectTextContainsLines(dotText, expectedDotTextLines); - } - - private void expectTextContainsLines(String dotText, - String[] expectedDotTextLines) { - for (String line : expectedDotTextLines) { - Assert.assertTrue("Not found:" + line, dotText.contains(line)); - } - } - - @Test - public void noReference() throws IOException { - GraphBuilder directoryBuilder = GraphBuilder - .getClassesDirectoryBuilder(); - directoryBuilder.setPackageExcludePatterns(""); - directoryBuilder.setPackageIncludePatterns("yooze.Class4"); - Graph graph = directoryBuilder.build("target/test-classes", - "yooze.Class4"); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000); - DotPrinter d = new DotPrinter(bytes); - d.print(graph); - String dotText = new String(bytes.toByteArray()); - System.out.println(dotText); - String[] expectedDotTextLines = { "digraph \"test-classes\" {", - "graph [size=100,100];", - "\"yooze.Class4\" [shape=box, height=0.0];", - "\"yooze.Class4\";", "}" }; - d.close(); - expectTextContainsLines(dotText, expectedDotTextLines); - } -} diff --git a/src/test/java/yooze/EarScannerTest.java b/src/test/java/yooze/EarScannerTest.java deleted file mode 100644 index 5371a74..0000000 --- a/src/test/java/yooze/EarScannerTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package yooze; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.util.List; - -import javassist.ClassPath; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import yooze.scanner.ArchiveScanner; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "classpath:applicationContext-test.xml") -public class EarScannerTest { - - @Autowired - private Config config; - - @Test - public void scanner() throws IOException { - List classpaths = new ArchiveScanner().scanArchive(config - .getEarFile()); - for (ClassPath path : classpaths) { - if (path instanceof DirClassPath) { - List classes = ((Inspectable) path).getClasses(); - assertThat(classes.size(), is(49)); - } - if (path instanceof JarClassPath) { - String name = ((Inspectable) path).getResourceName(); - if (name.contains("standard")) { - List classes = ((Inspectable) path).getClasses(); - assertTrue(classes.contains("org.apache.taglibs.standard.tag.common.sql.DataSourceUtil")); - } - } - } - } - - public void setConfig(Config config) { - this.config = config; - } - -} diff --git a/src/test/java/yooze/GraphTest.java b/src/test/java/yooze/GraphTest.java index bc0b6ab..10657f9 100644 --- a/src/test/java/yooze/GraphTest.java +++ b/src/test/java/yooze/GraphTest.java @@ -12,6 +12,7 @@ import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import yooze.application.GraphBuilderFactory; import yooze.domain.ClassModel; import yooze.domain.Graph; @@ -28,7 +29,7 @@ public class GraphTest { public void buildGraph() throws IOException { // new Yooze("/tmp/test").createNeoGraph("target/test-classes", // ".*?.Class."); - GraphBuilder libDirectoryBuilder = GraphBuilder + GraphBuilder libDirectoryBuilder = GraphBuilderFactory .getClassesDirectoryBuilder(); libDirectoryBuilder.setPackageIncludePatterns(".*?.Class."); libDirectoryBuilder.setPackageExcludePatterns(""); diff --git a/src/test/java/yooze/LargePackageTest.java b/src/test/java/yooze/LargePackageTest.java index 8451fa8..4407ec3 100644 --- a/src/test/java/yooze/LargePackageTest.java +++ b/src/test/java/yooze/LargePackageTest.java @@ -9,7 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import yooze.application.GraphBuilderFactory; import yooze.domain.Graph; +import yooze.output.DotPrinter; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext-test.xml") @@ -20,7 +22,7 @@ public class LargePackageTest { @Test public void largePackage() throws IOException { - GraphBuilder earBuilder = GraphBuilder.getEarBuilder(); + GraphBuilder earBuilder = GraphBuilderFactory.getEarBuilder(); earBuilder.setPackageIncludePatterns(""); earBuilder.setPackageExcludePatterns("java.*"); Graph graph = earBuilder.buildClassDepencyGraph(config.getEarFile(), diff --git a/src/test/java/yooze/MethodReferencesTest.java b/src/test/java/yooze/MethodReferencesTest.java index f8cb25e..bfb8ae7 100644 --- a/src/test/java/yooze/MethodReferencesTest.java +++ b/src/test/java/yooze/MethodReferencesTest.java @@ -13,7 +13,7 @@ import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import yooze.domain.Graph; +import yooze.application.GraphBuilderFactory; import yooze.domain.MethodModel; import yooze.dto.MethodDto; @@ -22,22 +22,18 @@ import yooze.dto.MethodDto; public class MethodReferencesTest { @Test public void test() throws IOException { - GraphBuilder directoryBuilder = GraphBuilder - .getClassesDirectoryBuilder(); + GraphBuilder directoryBuilder = GraphBuilderFactory.getClassesDirectoryBuilder(); directoryBuilder.setPackageIncludePatterns("yooze.Class.*"); - Graph graph = directoryBuilder.build("target/test-classes", - "yooze.Class1"); - MethodModel mm1 = MethodCache.getInstance() - .get("yooze.Class1.rup(int)"); + directoryBuilder.build("target/test-classes", "yooze.Class1"); + MethodModel mm1 = MethodCache.getInstance().get("yooze.Class1.rup(int)"); Assert.assertNotNull(mm1); MethodModel mm2 = MethodCache.getInstance().get("yooze.Class3.dof()"); Assert.assertNotNull(mm2); List callers = mm1.getCallers(); Assert.assertTrue(callers.contains(mm2)); - MethodDto dto = MethodDto.create(MethodCache.getInstance().get( - "yooze.Class1.zoef(yooze.Class2)")); - JsonGenerator jg = new ObjectMapper().getJsonFactory() - .createJsonGenerator(new FileOutputStream("c:\\ff\\out.json")); + MethodDto dto = MethodDto.create(MethodCache.getInstance().get("yooze.Class1.zoef(yooze.Class2)")); + JsonGenerator jg = new ObjectMapper().getJsonFactory().createJsonGenerator( + new FileOutputStream("c:\\ff\\out.json")); jg.writeObject(dto); jg.close(); } diff --git a/src/test/java/yooze/TgzBuilderTest.java b/src/test/java/yooze/TgzBuilderTest.java index ae3da15..baf4136 100644 --- a/src/test/java/yooze/TgzBuilderTest.java +++ b/src/test/java/yooze/TgzBuilderTest.java @@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import yooze.application.GraphBuilderFactory; import yooze.domain.ClassModel; import yooze.domain.Graph; @@ -23,7 +24,7 @@ public class TgzBuilderTest { @Test public void tgzBuilder() throws IOException { - GraphBuilder tgzBuilder = GraphBuilder.getDefaultTgzBuilder(); + GraphBuilder tgzBuilder = GraphBuilderFactory.getDefaultTgzBuilder(); tgzBuilder.setPackageIncludePatterns("nl.*"); tgzBuilder.setPackageExcludePatterns(""); Graph graph = tgzBuilder.buildClassDepencyGraph(config.getTgzFile(),