Posts Tagged pmd

Static Dependency Analysis

I recently started looking at a real mess of a project and want to start to try to understand how it was put together in terms of artifact dependencies.

Since it is at least using maven, my first thought was to use the poms to generate a dependency graph. After very little thinking, it was obvious that this was unreliable because of the potential for scenarios like this.

Say we have three modules A, B and C. The pom may declare a dependency from B to A (B->A) and from C to B (C->B). If in C there is a direct dependency to a class in A we won’t see that we have a dependency from C to A (C->A) because maven will compile using transitive dependency resolution. Of course, we should have an explicit dependency in the pom, but it is not required to build cuz C->B->A; only if the dependency from B to A (B->A) is cut, suddenly C fails to build.

I decided I needed to walk the source tree for each artifact. For each class, I would update a map that would map from full classname to artifactId and update the list of classes needed for the artifact based on the imports for the class.

Once all the artifacts have been evaluated, it’s time to create the artifact dependency graph and spit it out in so that Graphviz can create pretty (or horrific) pictures.

I hunted high and low for a package to parse java source code and finally settled on using PMD (http://pmd.sourceforge.net/) somewhat off-label for it’s AST parser.

Javadoc for it @ http://pmd.sourceforge.net/apidocs/net/sourceforge/pmd/parsers/Parser.html

I used PMD as as maven plugin back in the day so I was pretty confident it good kick that @$$!

As the Argentines say “let’s get it on!”

mvn archetype:create -DgroupId=javanal -DartifactId=importron -DpackageName=org.doom

 <dependency>
     <groupId>pmd</groupId>
     <artifactId>pmd</artifactId>
     <version>4.2.1</version>
 </dependency>

Since I am a lazy bastard, I will run it out of the AppTest.

I spanked this class up pdq:

    public class Artifact {
        String name
        Set< String > dependencies
        Set< Artifact > artifacts
    };

I will write about spanking classes later…

The app mostly fleshes out this map: Map< String, Artifact > classToArtifact which records which artifact a class (or package) lives in and the list of dependencies for each artifact, I will not waste your time with recursively walking a directory since it is very silly and a lot of fun I assume you will want to write it yourself, again. I think I’ve written this code about 27 times now.

Then, of course, there was a big fat ouch with the parser:

java.lang.NullPointerException
    at java.lang.String.indexOf(String.java:1564)
    at java.lang.String.indexOf(String.java:1546)
    at net.sourceforge.pmd.ast.JavaParserTokenManager.SkipLexicalActions(JavaParserTokenManager.ja
va:2049)
    at net.sourceforge.pmd.ast.JavaParserTokenManager.getNextToken(JavaParserTokenManager.java:200
0)
    at net.sourceforge.pmd.ast.JavaParser.jj_consume_token(JavaParser.java:9404)
    at net.sourceforge.pmd.ast.JavaParser.Statement(JavaParser.java:4013)
    at net.sourceforge.pmd.ast.JavaParser.BlockStatement(JavaParser.java:4159)
    at net.sourceforge.pmd.ast.JavaParser.Block(JavaParser.java:4120)
    at net.sourceforge.pmd.ast.JavaParser.MethodDeclaration(JavaParser.java:1401)
    at net.sourceforge.pmd.ast.JavaParser.ClassOrInterfaceBodyDeclaration(JavaParser.java:1070)
    at net.sourceforge.pmd.ast.JavaParser.ClassOrInterfaceBody(JavaParser.java:989)
    at net.sourceforge.pmd.ast.JavaParser.ClassOrInterfaceDeclaration(JavaParser.java:500)
    at net.sourceforge.pmd.ast.JavaParser.TypeDeclaration(JavaParser.java:392)
    at net.sourceforge.pmd.ast.JavaParser.CompilationUnit(JavaParser.java:151)
    at net.sourceforge.pmd.parsers.Java15Parser.parse(Java15Parser.java:24)
    at org.doom.App.parseFile(App.java:57)

Sadly, this happened quite a bit:

pass=299=0.665924 fail=150=0.334076 total=449

33% of the time in fact, which I have to say is hugely disappointing. I’m guessin’ it’s due to classpath issues.

So I guess I should go cry.

Instead I will put on the big boy pants and write a very dumb “parser” to pull out the package, classname and a list of import statements for each source file. Of course that makes me want to puke, but it is either that, give up or find another parser.

So… for each source file, I updated the map to show where that class / package lived and for each import I added a dependency for the artifact.

Once I walked all the source code, I used the dependencies of each artifact to look up the artifact it depended on. I called this “resolveArtifactDependencies”

Finally, I dumped everything on in graphviz format so I could see the graph.

Sweet! example dependency graph

For bigger projects it looks even crazier!

Comments (1)