diff --git a/example.png b/example.png new file mode 100644 index 0000000..87962ed Binary files /dev/null and b/example.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..02f2ab1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,135 @@ + + 4.0.0 + + nl.yooze + yooze + jar + 0.1-SNAPSHOT + yooze + www.cjib.nl + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + add-source + generate-sources + + add-test-source + + + + src/testdummies/java + + + + + + + + + + org.neo4j + neo4j + 1.4.M06 + + + commons-io + commons-io + 1.4 + + + junit + junit + 4.8.2 + jar + test + + + org.springframework + spring-beans + 3.0.5.RELEASE + jar + compile + + + + org.springframework + spring-test + 3.0.5.RELEASE + jar + compile + + + javax.annotation + jsr250-api + 1.0 + test + + + org.springframework + spring-context + 3.0.5.RELEASE + jar + compile + + + javassist + javassist + 3.12.1.GA + jar + compile + + + + ch.qos.logback + logback-core + 0.9.29 + jar + compile + + + + org.slf4j + slf4j-api + 1.6.1 + jar + compile + + + + ch.qos.logback + logback-classic + 0.9.29 + jar + compile + + + org.xeustechnologies + jtar + 1.0.4 + + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.10 + + + + diff --git a/src/._main b/src/._main new file mode 100644 index 0000000..3f98c2f Binary files /dev/null and b/src/._main differ diff --git a/src/main/java/yooze/ClassByteCountingInputStream.java b/src/main/java/yooze/ClassByteCountingInputStream.java new file mode 100644 index 0000000..f4ee304 --- /dev/null +++ b/src/main/java/yooze/ClassByteCountingInputStream.java @@ -0,0 +1,30 @@ +package yooze; + +import java.io.IOException; +import java.io.InputStream; + +public class ClassByteCountingInputStream extends InputStream { + + private InputStream nestedStream; + private String className; + private long count=0; + + public ClassByteCountingInputStream(String className, + InputStream inputStream) { + this.className=className; + this.nestedStream=inputStream; + } + + @Override + public int read() throws IOException { + count++; + return nestedStream.read(); + } + + @Override + public void close() throws IOException { + nestedStream.close(); + Statistics.addBytecodeSizeForClass(className, count); + } + +} diff --git a/src/main/java/yooze/ClassCache.java b/src/main/java/yooze/ClassCache.java new file mode 100644 index 0000000..d26a2dd --- /dev/null +++ b/src/main/java/yooze/ClassCache.java @@ -0,0 +1,28 @@ +package yooze; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import yooze.domain.ClassModel; + +public class ClassCache { + private final static Map entries = new ConcurrentHashMap(); + + public static boolean contains(String classname) { + return entries.containsKey(classname); + } + + public static ClassModel createNewDummyModel(String name) { + ClassModel classModel = new ClassModel(name); + entries.put(name, classModel); + return classModel; + } + + public static ClassModel get(String className) { + return entries.get(className); + } + + public static void add(String className, ClassModel model) { + entries.put(className, model); + } +} diff --git a/src/main/java/yooze/ClassModelBuilder.java b/src/main/java/yooze/ClassModelBuilder.java new file mode 100644 index 0000000..66f97e7 --- /dev/null +++ b/src/main/java/yooze/ClassModelBuilder.java @@ -0,0 +1,177 @@ +package yooze; + +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; +import javassist.bytecode.ConstPool; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import yooze.domain.ClassModel; +import yooze.domain.MethodCallModel; +import yooze.domain.MethodModel; + +/** + * Builds a ClassModel. + */ +public class ClassModelBuilder { + private static Logger log = LoggerFactory.getLogger(ClassModelBuilder.class); + + private Pattern[] packageIncludePatterns; + private Pattern[] packageExcludePatterns; + private ClassPool pool; + + public ClassModelBuilder(ClassPool pool) { + this.pool = pool; + } + + public ClassModel scanClassOrSkip(String className) { + if (shouldSkip(className)) + return null; + if (ClassCache.contains(className)) { + return ClassCache.get(className); + } + log.info("scanning {}", className); + + return scan(className); + + } + + 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; + } + } + + private ClassModel tryScan(String className, ClassModel model) throws NotFoundException { + CtClass ctClass = pool.get(className); + if (isScannable(ctClass)) { + ConstPool constPool = ctClass.getClassFile().getConstPool(); + + addClassReferences(model, constPool); + addMethods(model, ctClass); + resolveMethodReferences(); + return model; + } else { + return null; + } + } + + private boolean isScannable(CtClass ctClass) { + return !ctClass.isFrozen(); + } + + private void resolveMethodReferences() { + for (MethodModel method : MethodCache.getInstance().getMethods()) { + for (MethodCallModel methodCall : method.getMethodCalls()) { + MethodModel calledMethod = methodCall.getCalledMethod(); + if (calledMethod != null && calledMethod != method) { + calledMethod.addCaller(method); + } + } + } + } + + private void addMethods(ClassModel containingClass, CtClass ctClass) { + CtMethod[] methods = ctClass.getMethods(); + for (CtMethod method : methods) { + containingClass.addMethod(MethodModel.create(containingClass, method)); + } + } + + private void addClassReferences(ClassModel model, ConstPool constPool) { + Set classNames = constPool.getClassNames(); + for (String classResourcename : classNames) { + String refClassName = Util.toClassName(classResourcename); + addClassReference(model, refClassName); + } + } + + private void addClassReference(ClassModel model, String refClassName) { + /* recursive invocation */ + ClassModel scannedClass = scanClassOrSkip(refClassName); + if (scannedClass != null && !scannedClass.equals(model)) { + model.addReference(scannedClass); + } + } + + private boolean shouldSkip(String className) { + if (!isIncluded(className)) { + log.debug("skipping {}", className); + return true; + } + if (isExcluded(className)) { + log.debug("skipping {}", className); + return true; + } + + // if (isInnerClass(className)) { + // log.debug("skipping inner class {}", className); + // return true; + // } + // + // if (className.equals("java.lang.Object")) { + // return true; + // } + return false; + } + + private boolean isInnerClass(String className) { + return className.contains("$"); + } + + private boolean isExcluded(String className) { + if (packageExcludePatterns != null) { + for (Pattern excludePattern : packageExcludePatterns) { + Matcher matcher = excludePattern.matcher(className); + if (matcher.matches()) { + return true; + } + } + } + return false; + } + + private boolean isIncluded(String className) { + if (packageIncludePatterns != null) { + for (Pattern includePattern : packageIncludePatterns) { + Matcher matcher = includePattern.matcher(className); + if (matcher.matches()) { + return true; + } + } + } + return false; + } + + public void setPackageIncludePatterns(String... packageIncludePatterns) { + if (packageIncludePatterns != null) { + this.packageIncludePatterns = new Pattern[packageIncludePatterns.length]; + int i = 0; + for (String pattern : packageIncludePatterns) { + this.packageIncludePatterns[i++] = Pattern.compile(pattern); + } + } + } + + public void setPackageExcludePatterns(String... packageExcludePatterns) { + if (packageExcludePatterns != null) { + this.packageExcludePatterns = new Pattern[packageExcludePatterns.length]; + int i = 0; + for (String pattern : packageExcludePatterns) { + this.packageExcludePatterns[i++] = Pattern.compile(pattern); + } + } + } +} diff --git a/src/main/java/yooze/ClassesDirScanner.java b/src/main/java/yooze/ClassesDirScanner.java new file mode 100644 index 0000000..724cddd --- /dev/null +++ b/src/main/java/yooze/ClassesDirScanner.java @@ -0,0 +1,28 @@ +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 new file mode 100644 index 0000000..bc1c88c --- /dev/null +++ b/src/main/java/yooze/DirClassPath.java @@ -0,0 +1,90 @@ +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 new file mode 100644 index 0000000..a0f809b --- /dev/null +++ b/src/main/java/yooze/DotPrinter.java @@ -0,0 +1,59 @@ +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 new file mode 100644 index 0000000..478d796 --- /dev/null +++ b/src/main/java/yooze/GraphBuilder.java @@ -0,0 +1,105 @@ +package yooze; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +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. + */ +public class GraphBuilder { + + private Scanner scanner; + private ClassModelBuilder classModelBuilder; + 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) { + super(); + this.scanner = scanner; + } + + public Graph build(String archive) throws IOException { + return buildClassDepencyGraph(new File(archive)); + } + + public Graph buildClassDepencyGraph(File archiveFile) throws IOException { + List cpList = scanner.scanArchive(archiveFile); + + ClassPool pool = ClassPool.getDefault(); + for (ClassPath cp : cpList) { + pool.appendClassPath(cp); + } + + Graph graph = createClassDependencyGraph(pool, cpList); + graph.setName(archiveFile.getName()); + return graph; + } + + private Graph createClassDependencyGraph(ClassPool pool, List classpath) { + 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 className : classes) { + ClassModel newModel = classModelBuilder.scanClassOrSkip(className); + if (newModel != null) { + graph.add(newModel); + } + } + } + return graph; + } + + public void setPackageIncludePatterns(String... packageIncludePatterns) { + this.packageIncludePatterns = packageIncludePatterns; + } + + public void setPackageExcludePatterns(String... packageExcludePatterns) { + this.packageExcludePatterns = packageExcludePatterns; + } + +} diff --git a/src/main/java/yooze/Inspectable.java b/src/main/java/yooze/Inspectable.java new file mode 100644 index 0000000..07bb261 --- /dev/null +++ b/src/main/java/yooze/Inspectable.java @@ -0,0 +1,8 @@ +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 new file mode 100644 index 0000000..27712d6 --- /dev/null +++ b/src/main/java/yooze/JarClassPath.java @@ -0,0 +1,78 @@ +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/MethodCache.java b/src/main/java/yooze/MethodCache.java new file mode 100644 index 0000000..72e464a --- /dev/null +++ b/src/main/java/yooze/MethodCache.java @@ -0,0 +1,32 @@ +package yooze; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import yooze.domain.MethodModel; + +public class MethodCache { + private final Map entries = new ConcurrentHashMap(); + private final static MethodCache instance = new MethodCache(); + + public static MethodCache getInstance() { + return instance; + } + + public boolean contains(String classname) { + return entries.containsKey(classname); + } + + public MethodModel get(String fullName) { + return entries.get(fullName); + } + + public void add(MethodModel methodmodel) { + entries.put(methodmodel.getFullname(), methodmodel); + } + + public Collection getMethods() { + return entries.values(); + } +} diff --git a/src/main/java/yooze/Statistics.java b/src/main/java/yooze/Statistics.java new file mode 100644 index 0000000..e938ce2 --- /dev/null +++ b/src/main/java/yooze/Statistics.java @@ -0,0 +1,16 @@ +package yooze; + +import java.util.concurrent.ConcurrentHashMap; + +public class Statistics { + private static ConcurrentHashMap bytecodeSizes=new ConcurrentHashMap(); + + public static void addBytecodeSizeForClass(String className, long size){ + bytecodeSizes.put(className, size); + } + + public static Long getByteCodeSizeForClass(String className){ + Long value = bytecodeSizes.get(className); + return value==null?0:value; + } +} diff --git a/src/main/java/yooze/Util.java b/src/main/java/yooze/Util.java new file mode 100644 index 0000000..27b5274 --- /dev/null +++ b/src/main/java/yooze/Util.java @@ -0,0 +1,39 @@ +package yooze; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.apache.commons.io.IOUtils; + +public class Util { + private final static Pattern fileNamePattern = Pattern.compile(".+/(.+)"); + + public static File extractFile(ZipFile file, ZipEntry entry) + throws IOException { + String name=entry.getName(); + Matcher m = fileNamePattern.matcher(name); + if (m.matches()){//chop off path + name=m.group(1); + } + + File tempFile = File.createTempFile(name, ".file"); + InputStream in = file.getInputStream(entry); + FileOutputStream out = new FileOutputStream(tempFile); + IOUtils.copy(in, out); + return tempFile; + } + + public static String toClassResource(String className) { + return className.replaceAll("\\.", "/")+".class"; + } + + public static String toClassName(String classResource) { + return classResource.replaceAll("/", "."); + } +} diff --git a/src/main/java/yooze/domain/ClassModel.java b/src/main/java/yooze/domain/ClassModel.java new file mode 100644 index 0000000..694a04a --- /dev/null +++ b/src/main/java/yooze/domain/ClassModel.java @@ -0,0 +1,67 @@ +package yooze.domain; + +import java.util.ArrayList; +import java.util.List; + + +/** + * + */ +public class ClassModel { + final private String name; + + final private List references = new ArrayList(); + final private List methods = new ArrayList(); + + public ClassModel(String name) { + super(); + this.name = name; + } + + public void addReference(ClassModel classModel) { + references.add(classModel); + } + + public void addMethod(MethodModel method) { + methods.add(method); + } + + public String getName() { + return name; + } + + public List getReferences() { + return new ArrayList(references); + } + + @Override + public String toString() { + return "class " + name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ClassModel other = (ClassModel) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + +} diff --git a/src/main/java/yooze/domain/Graph.java b/src/main/java/yooze/domain/Graph.java new file mode 100644 index 0000000..01385b1 --- /dev/null +++ b/src/main/java/yooze/domain/Graph.java @@ -0,0 +1,28 @@ +package yooze.domain; + +import java.util.ArrayList; +import java.util.List; + + +public class Graph { + private String name; + + private final List classes = new ArrayList(); + + public void add(ClassModel model) { + classes.add(model); + } + + public List getChildren() { + return classes; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/yooze/domain/MethodCallModel.java b/src/main/java/yooze/domain/MethodCallModel.java new file mode 100644 index 0000000..5c1fccd --- /dev/null +++ b/src/main/java/yooze/domain/MethodCallModel.java @@ -0,0 +1,32 @@ +package yooze.domain; + +import javassist.NotFoundException; +import javassist.expr.MethodCall; +import yooze.ClassCache; +import yooze.MethodCache; + +public class MethodCallModel { + private MethodCall methodCall; + + public MethodCallModel(MethodCall m) { + this.methodCall = m; + } + + public ClassModel getCalledClass() { + return ClassCache.get(methodCall.getClassName()); + } + + public MethodModel getCalledMethod() { + try { + ParameterList parameterList = ParameterList.create(methodCall.getMethod()); + return MethodCache.getInstance().get( + createQualifiedMethodname(parameterList)); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + private String createQualifiedMethodname(ParameterList parameterList) { + return methodCall.getClassName() + "." + methodCall.getMethodName() + "(" + parameterList.asText() + ")"; + } +} diff --git a/src/main/java/yooze/domain/MethodModel.java b/src/main/java/yooze/domain/MethodModel.java new file mode 100644 index 0000000..dbd4d9c --- /dev/null +++ b/src/main/java/yooze/domain/MethodModel.java @@ -0,0 +1,91 @@ +package yooze.domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javassist.CannotCompileException; +import javassist.CtMethod; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; +import yooze.MethodCache; + +public class MethodModel { + private ClassModel containingClass; + private String name; + private ParameterList parameterList; + private final List methodCalls = new ArrayList(); + private final Set callers = new HashSet(); + + public static MethodModel create(ClassModel containingClass, CtMethod method) { + MethodModel methodModel = new MethodModel(containingClass, method); + MethodCache.getInstance().add(methodModel); + methodModel.scanSubsequentMethodCalls(method); + return methodModel; + } + + private MethodModel(ClassModel containingClass, CtMethod method) { + this.containingClass = containingClass; + name = method.getName(); + parameterList = ParameterList.create(method); + } + + public void addCaller(MethodModel method) { + callers.add(method); + } + + public List getCallers() { + return new ArrayList(callers); + } + + public List getMethodCalls() { + return methodCalls; + } + + private void scanSubsequentMethodCalls(CtMethod method) { + try { + method.instrument(new ExprEditor() { + @Override + public void edit(MethodCall m) throws CannotCompileException { + MethodCallModel methodCallModel = new MethodCallModel(m); + methodCalls.add(methodCallModel); + } + }); + } catch (CannotCompileException e) { + } + } + + public String getName() { + return name; + } + + public String getFullname() { + return containingClass.getName() + "." + name + "(" + parameterList.asText() + ")"; + } + + @Override + public String toString() { + return getFullname(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MethodModel)) { + return false; + } + MethodModel other = (MethodModel) obj; + return this.getFullname().equals(other.getFullname()); + } + + @Override + public int hashCode() { + return getFullname().hashCode(); + } +} diff --git a/src/main/java/yooze/domain/ParameterList.java b/src/main/java/yooze/domain/ParameterList.java new file mode 100644 index 0000000..9c2f39b --- /dev/null +++ b/src/main/java/yooze/domain/ParameterList.java @@ -0,0 +1,58 @@ +package yooze.domain; + +import java.util.ArrayList; +import java.util.List; + +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; + +public class ParameterList { + + private List parameters = new ArrayList(); + + public static ParameterList create(CtMethod method) { + ParameterList parameterList = new ParameterList(); + parameterList.parameters = getParameters(method); + return parameterList; + } + + private static List getParameters(CtMethod method) { + try { + List parameters = new ArrayList(); + CtClass[] parameterTypes = method.getParameterTypes(); + for (CtClass parameterType : parameterTypes) { + parameters.add(new ParameterModel(parameterType)); + } + return parameters; + + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + + public String asText() { + String parameterText = ""; + int index = 0; + int size = parameters.size(); + for (ParameterModel parameter : parameters) { + parameterText = addType(parameterText, parameter); + parameterText = addComma(parameterText, index, size); + index++; + } + return parameterText; + } + + private String addComma(String parameterText, int i, int size) { + if (i < (size - 1)) { + parameterText += ", "; + } + return parameterText; + } + + private String addType(String parameterText, ParameterModel parameter) { + parameterText += parameter.getType().getName(); + return parameterText; + } + +} diff --git a/src/main/java/yooze/domain/ParameterModel.java b/src/main/java/yooze/domain/ParameterModel.java new file mode 100644 index 0000000..34893da --- /dev/null +++ b/src/main/java/yooze/domain/ParameterModel.java @@ -0,0 +1,22 @@ +package yooze.domain; + +import yooze.ClassCache; +import javassist.CtClass; + +public class ParameterModel { + private ClassModel type; + private String name; + + public ParameterModel(CtClass typeAsCtClass) { + String classname = typeAsCtClass.getName(); + type = ClassCache.get(classname); + if (type == null) { + type = ClassCache.createNewDummyModel(classname); + } + name = "";// javassist doesn't give me this (?) + } + + public ClassModel getType() { + return type; + } +} diff --git a/src/main/java/yooze/dto/MethodDto.java b/src/main/java/yooze/dto/MethodDto.java new file mode 100644 index 0000000..d047dc7 --- /dev/null +++ b/src/main/java/yooze/dto/MethodDto.java @@ -0,0 +1,41 @@ +package yooze.dto; + +import java.util.ArrayList; +import java.util.List; + +import yooze.domain.MethodModel; + +public class MethodDto { + private String name; + private List callers = new ArrayList(); + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public List getCallers() { + return callers; + } + + public void setCallers(List callers) { + this.callers = callers; + } + + public void addCaller(MethodDto caller) { + callers.add(caller); + } + + public static MethodDto create(MethodModel startMethod) { + MethodDto methodDto = new MethodDto(); + methodDto.setName(startMethod.getFullname()); + for (MethodModel caller : startMethod.getCallers()) { + methodDto.addCaller(create(caller)); + } + return methodDto; + } + +} diff --git a/src/main/java/yooze/etc/ClassBuilder.java b/src/main/java/yooze/etc/ClassBuilder.java new file mode 100644 index 0000000..2751b28 --- /dev/null +++ b/src/main/java/yooze/etc/ClassBuilder.java @@ -0,0 +1,48 @@ +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 new file mode 100644 index 0000000..ad08c2d --- /dev/null +++ b/src/main/java/yooze/etc/Neo4jDao.java @@ -0,0 +1,89 @@ +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 new file mode 100644 index 0000000..30a4660 --- /dev/null +++ b/src/main/java/yooze/etc/Yooze.java @@ -0,0 +1,41 @@ +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]; + new Yooze(neoDb).createNeoGraph(earfile, in,ex); + + } + + public Yooze(String neo4jDb) { + super(); + this.neo4jDb = neo4jDb; + } + + public void createNeoGraph(String archive, String packageIncludePatterns, String packageExcludePatterns) + throws IOException{ + GraphBuilder libDirectoryBuilder = GraphBuilder.getLibDirectoryBuilder(); + libDirectoryBuilder.setPackageExcludePatterns(packageExcludePatterns); + libDirectoryBuilder.setPackageIncludePatterns(packageIncludePatterns); + + Graph graph = libDirectoryBuilder.build(archive); + + 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 new file mode 100644 index 0000000..6f14aa4 --- /dev/null +++ b/src/main/java/yooze/scanner/ArchiveScanner.java @@ -0,0 +1,192 @@ +package yooze.scanner; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +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.Util; + +/** + * @author shautvast 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)"); + + public List scanArchive(String archiveName) throws IOException { + return scanArchive(new File(archiveName)); + } + + public List scanArchive(File archiveFile) throws IOException { + 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 result; + } + + JarFile archive = new JarFile(archiveFile); + if (archive.getName().endsWith(".ear")) { + result.addAll(scanRoot(archive)); + result.addAll(scanWars(getWars(archive))); + + } else if (archive.getName().endsWith(".war")) { + result.addAll(scanWars(Arrays.asList(new JarFile[] { archive }))); + } else { + // treat as jar file + result.add(new JarClassPath(archive)); + } + 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) { + try { + FileOutputStream out = new FileOutputStream(classFile); + IOUtils.copy(warfile.getInputStream(entry), out); + return classFile; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + 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();) { + JarEntry entry = (JarEntry) entries.nextElement(); + if (isWarfile(entry)) { + try { + File warFile = Util.extractFile(earfile, entry); + wars.add(new JarFile(warFile)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return wars; + } + + private boolean isWarfile(JarEntry entry) { + return entry.getName().endsWith(".war"); + } + + 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); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return classpaths; + } + + private boolean isJarfile(JarEntry entry) { + return entry.getName().endsWith(".jar"); + } + +} diff --git a/src/main/java/yooze/scanner/LibScanner.java b/src/main/java/yooze/scanner/LibScanner.java new file mode 100644 index 0000000..bd0747d --- /dev/null +++ b/src/main/java/yooze/scanner/LibScanner.java @@ -0,0 +1,47 @@ +package yooze.scanner; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarFile; + +import yooze.JarClassPath; + +import javassist.ClassPath; + +/** + * Scans a directory ("lib") recursively for jarfiles and adds them to the + * classpath + * + * @author sander + * + */ +public class LibScanner implements Scanner { + + @Override + 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); + } + + private List doScanArchive(List classpaths, File file) + throws IOException { + + File[] entries = file.listFiles(); + for (File entry : entries) { + if (entry.isDirectory()) { + doScanArchive(classpaths, entry); + } else if (entry.getName().endsWith(".jar")) { + classpaths.add(new JarClassPath(new JarFile(entry))); + } + } + return classpaths; + } + +} diff --git a/src/main/java/yooze/scanner/Scanner.java b/src/main/java/yooze/scanner/Scanner.java new file mode 100644 index 0000000..d7883ae --- /dev/null +++ b/src/main/java/yooze/scanner/Scanner.java @@ -0,0 +1,13 @@ +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 new file mode 100644 index 0000000..bb9302d --- /dev/null +++ b/src/main/java/yooze/scanner/TgzScanner.java @@ -0,0 +1,65 @@ +package yooze.scanner; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +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; + +/** + * @author sander + * + */ +public class TgzScanner implements Scanner { + 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)))); + 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"); + FileOutputStream fos = new FileOutputStream(tempFile); + BufferedOutputStream dest = new BufferedOutputStream(fos); + + while ((count = tarInputStream.read(data)) != -1) { + dest.write(data, 0, count); + } + dest.flush(); + dest.close(); + classpaths.add(new JarClassPath(new JarFile(tempFile))); + } + } + tarInputStream.close(); + return classpaths; + } + + private String singleName(String name) { + int slash = name.lastIndexOf("/"); + if (slash > -1) { + return name.substring(slash + 1); + } else { + return name; + } + } +} diff --git a/src/test/java/yooze/AssTest.java b/src/test/java/yooze/AssTest.java new file mode 100644 index 0000000..965b95b --- /dev/null +++ b/src/test/java/yooze/AssTest.java @@ -0,0 +1,22 @@ +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/Config.java b/src/test/java/yooze/Config.java new file mode 100644 index 0000000..e2f3952 --- /dev/null +++ b/src/test/java/yooze/Config.java @@ -0,0 +1,26 @@ +package yooze; + +import java.io.File; + +public class Config { + private File earFile; + private File tgzFile; + + public void setEarFile(File earFile) { + this.earFile = earFile; + } + + public File getEarFile() { + return earFile; + } + + public File getTgzFile() { + return tgzFile; + } + + public void setTgzFile(File tgzFile) { + this.tgzFile = tgzFile; + } + + +} diff --git a/src/test/java/yooze/DotPrinterTest.java b/src/test/java/yooze/DotPrinterTest.java new file mode 100644 index 0000000..2f84733 --- /dev/null +++ b/src/test/java/yooze/DotPrinterTest.java @@ -0,0 +1,64 @@ +package yooze; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +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 { + + @Test + public void dotPrinting() throws IOException { + GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder(); + directoryBuilder.setPackageExcludePatterns(".*?Class4"); + directoryBuilder.setPackageIncludePatterns(".*?.Class."); + Graph graph = directoryBuilder.build("target/test-classes"); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(500); + DotPrinter d = new DotPrinter(bytes); + d.print(graph); + String dotText = new String(bytes.toByteArray()); + d.close(); + String expectedDotText = "digraph \"test-classes\" {\r\n" // + + "graph [size=100,100];\r\n" + + "\"yooze.Class1\" [shape=box, height=0.0];\r\n" // + + "\"yooze.Class1\" -> \"yooze.Class2\"\r\n" + + "\"yooze.Class2\" [shape=box, height=0.0];\r\n" + + "\"yooze.Class2\" -> \"yooze.Class3\"\r\n" + + "\"yooze.Class3\" [shape=box, height=0.0];\r\n" + + "\"yooze.Class3\" -> \"yooze.Class1\"\r\n" + + "\"yooze.Class1\" [shape=box, height=0.0];\r\n" + + "\"yooze.Class2\" [shape=box, height=0.0];\r\n" // + + "\"yooze.Class3\" [shape=box, height=0.0];\r\n" + "}\r\n"; + + assertEquals(expectedDotText, dotText); + } + + @Test + public void noReference() throws IOException { + GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder(); + directoryBuilder.setPackageExcludePatterns(""); + directoryBuilder.setPackageIncludePatterns("yooze.Class4"); + Graph graph = directoryBuilder.build("target/test-classes"); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000); + DotPrinter d = new DotPrinter(bytes); + d.print(graph); + String dotText = new String(bytes.toByteArray()); + String expectedDotText = "digraph \"test-classes\" {\r\n" // + + "graph [size=100,100];\r\n"// + + "\"yooze.Class4\" [shape=box, height=0.0];\r\n" // + + "\"yooze.Class4\";\r\n" // + + "}\r\n"; + d.close(); + assertEquals(expectedDotText, dotText); + } +} diff --git a/src/test/java/yooze/EarScannerTest.java b/src/test/java/yooze/EarScannerTest.java new file mode 100644 index 0000000..5371a74 --- /dev/null +++ b/src/test/java/yooze/EarScannerTest.java @@ -0,0 +1,50 @@ +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 new file mode 100644 index 0000000..8c7058c --- /dev/null +++ b/src/test/java/yooze/GraphTest.java @@ -0,0 +1,40 @@ +package yooze; + +import java.io.IOException; + +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.ClassModel; +import yooze.domain.Graph; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext-test.xml") +public class GraphTest { + + @Test + public void buildGraph() throws IOException{ +// new Yooze("/tmp/test").createNeoGraph("target/test-classes", ".*?.Class."); + GraphBuilder libDirectoryBuilder = GraphBuilder.getLibDirectoryBuilder(); + libDirectoryBuilder.setPackageIncludePatterns(".*?.Class."); + libDirectoryBuilder.setPackageExcludePatterns(""); + Graph graph = libDirectoryBuilder.build("target/test-classes"); + ClassModel class1=graph.getChildren().get(0); + + assertTrue(class1!=null); + ClassModel class2Dummy=new ClassModel("yooze.Class2"); + assertTrue(class1.getReferences().contains(class2Dummy)); + + ClassModel class2=class1.getReferences().get(0); + assertTrue (class2.getName().equals("yooze.Class2")); + ClassModel class3=class2.getReferences().get(0); + assertTrue (class3.getName().equals("yooze.Class3")); + + assertTrue(class2.getReferences().contains(class3)); + assertTrue(class3.getReferences().contains(class1)); + + } +} diff --git a/src/test/java/yooze/LargePackageTest.java b/src/test/java/yooze/LargePackageTest.java new file mode 100644 index 0000000..c7127e4 --- /dev/null +++ b/src/test/java/yooze/LargePackageTest.java @@ -0,0 +1,40 @@ +package yooze; + +import java.io.FileOutputStream; +import java.io.IOException; + +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.domain.Graph; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext-test.xml") +public class LargePackageTest { + + @Autowired + private Config config; + + @Test + public void largePackage() throws IOException{ + GraphBuilder earBuilder = GraphBuilder.getEarBuilder(); + earBuilder.setPackageIncludePatterns(""); + earBuilder.setPackageExcludePatterns("java.*"); + Graph graph = earBuilder.buildClassDepencyGraph(config.getEarFile()); + new DotPrinter(new FileOutputStream("/tmp/example.dot")).print(graph); + + } + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + +} diff --git a/src/test/java/yooze/MethodReferencesTest.java b/src/test/java/yooze/MethodReferencesTest.java new file mode 100644 index 0000000..c016824 --- /dev/null +++ b/src/test/java/yooze/MethodReferencesTest.java @@ -0,0 +1,40 @@ +package yooze; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import junit.framework.Assert; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.map.ObjectMapper; +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; +import yooze.domain.MethodModel; +import yooze.dto.MethodDto; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext-test.xml") +public class MethodReferencesTest { + @Test + public void test() throws IOException { + GraphBuilder directoryBuilder = GraphBuilder.getClassesDirectoryBuilder(); + directoryBuilder.setPackageIncludePatterns("yooze.Class.*"); + Graph graph = directoryBuilder.build("target/test-classes"); + 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")); + jg.writeObject(dto); + jg.close(); + } +} diff --git a/src/test/java/yooze/TgzBuilderTest.java b/src/test/java/yooze/TgzBuilderTest.java new file mode 100644 index 0000000..05630f0 --- /dev/null +++ b/src/test/java/yooze/TgzBuilderTest.java @@ -0,0 +1,52 @@ +package yooze; + +import java.io.IOException; +import java.util.ArrayList; + +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.domain.ClassModel; +import yooze.domain.Graph; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:applicationContext-test.xml") +public class TgzBuilderTest { + + @Autowired + private Config config; + + @Test + public void tgzBuilder() throws IOException { + GraphBuilder tgzBuilder = GraphBuilder.getDefaultTgzBuilder(); + tgzBuilder.setPackageIncludePatterns("nl.*"); + tgzBuilder.setPackageExcludePatterns(""); + Graph graph = tgzBuilder.buildClassDepencyGraph(config.getTgzFile()); + + ArrayList names = new ArrayList(); + + for (ClassModel cm : graph.getChildren()) { + names.add(cm.getName()); + } + assertTrue(names.contains("nl.jssl.jas.Main")); + assertTrue(names.contains("nl.jssl.jas.agent.Agent")); + assertTrue(names + .contains("nl.jssl.jas.instrumentation.ClassTransformer")); + assertTrue(names + .contains("nl.jssl.jas.instrumentation.JavassistInstrumenter")); + assertTrue(names.contains("nl.jssl.jas.measurement.Measurement")); + assertTrue(names.contains("nl.jssl.jas.measurement.Stopwatch")); + assertTrue(names.contains("nl.jssl.testjas.TestClass")); + assertTrue(names.contains("nl.jssl.testjas.Instrument")); + assertTrue(names.contains("nl.jssl.testjas.AgentTest")); + + } + + public void setConfig(Config config) { + this.config = config; + } +} diff --git a/src/test/resources/.DS_Store b/src/test/resources/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/test/resources/.DS_Store differ diff --git a/src/test/resources/._.DS_Store b/src/test/resources/._.DS_Store new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/src/test/resources/._.DS_Store differ diff --git a/src/test/resources/agent.tar.gz b/src/test/resources/agent.tar.gz new file mode 100644 index 0000000..95dba71 Binary files /dev/null and b/src/test/resources/agent.tar.gz differ diff --git a/src/test/resources/applicationContext-test.xml b/src/test/resources/applicationContext-test.xml new file mode 100644 index 0000000..bc05591 --- /dev/null +++ b/src/test/resources/applicationContext-test.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/src/test/resources/examples.ear b/src/test/resources/examples.ear new file mode 100644 index 0000000..03e43c8 Binary files /dev/null and b/src/test/resources/examples.ear differ diff --git a/src/test/resources/examples.war b/src/test/resources/examples.war new file mode 100644 index 0000000..bf0e058 Binary files /dev/null and b/src/test/resources/examples.war differ diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 0000000..2a6fe54 --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,12 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/src/testdummies/java/yooze/Class1.java b/src/testdummies/java/yooze/Class1.java new file mode 100644 index 0000000..4c49bc3 --- /dev/null +++ b/src/testdummies/java/yooze/Class1.java @@ -0,0 +1,15 @@ +package yooze; + +public class Class1 { + Class2 class2; + + public void rup(int k) { + System.out.println(k); + class2.annoy(); + zoef(class2); + } + + public void zoef(Class2 envy) { + System.out.println(envy); + } +} diff --git a/src/testdummies/java/yooze/Class2.java b/src/testdummies/java/yooze/Class2.java new file mode 100644 index 0000000..8c18115 --- /dev/null +++ b/src/testdummies/java/yooze/Class2.java @@ -0,0 +1,10 @@ +package yooze; + +public class Class2 { + public static Class3 class3=new Class3(); + + public void annoy() { + // TODO Auto-generated method stub + + } +} diff --git a/src/testdummies/java/yooze/Class3.java b/src/testdummies/java/yooze/Class3.java new file mode 100644 index 0000000..e911bcf --- /dev/null +++ b/src/testdummies/java/yooze/Class3.java @@ -0,0 +1,14 @@ +package yooze; + +public class Class3 { + public static Class1 class1 = new Class1(); + + public static void annoyStatic() { + // TODO Auto-generated method stub + + } + + public void dof() { + class1.rup(1); + } +} diff --git a/src/testdummies/java/yooze/Class4.java b/src/testdummies/java/yooze/Class4.java new file mode 100644 index 0000000..3a5601d --- /dev/null +++ b/src/testdummies/java/yooze/Class4.java @@ -0,0 +1,8 @@ +package yooze; + +public class Class4 { + + public void fla() { + new Class3().dof(); + } +}