Introduction

Recently I came over a great visualization of the relationships between classes made by Mike Bostock with his Hierarchical Edge Bundling in D3:

I wondered how hard it would be to reimplement this visualization with jQAssistant and Neo4j and show actual dependencies between Java types. So let's have a look!

Idea

If you've scanned your project with jQAssistant, you get some graph data like the following:

The graph contains the information about the dependencies of all software entities like Java classes or interfaces. This information is exactly what we need to create a Dependency Analysis between Java types. We just have to create the right Cypher query and the result that fits into the D3 visualization above.

Getting the right Data

What you need to do is to scan your software with jQAssistant to get the information about the dependencies between classes. Just download the demo project at https://github.com/buschmais/spring-petclinic, build it with Java & Maven (mvn clean install) and start the embedded Neo4j graph database with mvn jqassistant:server .

With a running Neo4j database, we connect to it via py2neo and get the relationship information between nodes. In this example, we want to retrieve the direct dependency between any Java type that belongs to our application:

  • We identify our application's type's by using only artifacts that are contained in our project by using the corresponding node labels Project and Artifact.
  • We also filter out any test classes by skipping the non-relevant artifacts of the type test-jar.
  • With the remaining types, we search for all direct dependencies with the DEPENDS_ON relationship to the other types of our application. For reasons of completeness, we also provide the types that don't depend on any other type of our application. That's why we specify the *0..1 parameter in the relationship.
  • Finally, we COLLECT all the dependencies for one type into a list (more exact, the fqn aka full qualified name of the type), because that's what the D3 visualization needs as input.

The result is a dictionary with all the information needed for the D3 visualization.


In [1]:
import py2neo

query="""
MATCH
    (:Project)-[:CONTAINS]->(artifact:Artifact)-[:CONTAINS]->(type:Type)
WHERE
    // we don't want to analyze test artifacts
    NOT artifact.type = "test-jar" 
WITH DISTINCT type, artifact
MATCH
    (type)-[:DEPENDS_ON*0..1]->(directDependency:Type)<-[:CONTAINS]-(artifact)
RETURN type.fqn as name, COLLECT(DISTINCT directDependency.fqn) as imports
"""

json_data = py2neo.Graph().run(query).data()
json_data[:3]


Out[1]:
[{'imports': ['org.springframework.samples.petclinic.model.Pet',
   'org.springframework.samples.petclinic.web.PetValidator'],
  'name': 'org.springframework.samples.petclinic.web.PetValidator'},
 {'imports': ['org.springframework.samples.petclinic.repository.jdbc.JdbcPetVisitExtractor',
   'org.springframework.samples.petclinic.repository.OwnerRepository',
   'org.springframework.samples.petclinic.model.Owner',
   'org.springframework.samples.petclinic.util.EntityUtils',
   'org.springframework.samples.petclinic.repository.jdbc.JdbcOwnerRepositoryImpl',
   'org.springframework.samples.petclinic.model.PetType',
   'org.springframework.samples.petclinic.repository.jdbc.JdbcPet'],
  'name': 'org.springframework.samples.petclinic.repository.jdbc.JdbcOwnerRepositoryImpl'},
 {'imports': ['org.springframework.samples.petclinic.util.EntityUtils',
   'org.springframework.samples.petclinic.model.BaseEntity'],
  'name': 'org.springframework.samples.petclinic.util.EntityUtils'}]

We just write that data to a file that we read into our D3 HTML-Template.


In [2]:
import json

with open ( "vis/flare-imports.json", mode='w') as json_file:
    json_file.write(json.dumps(json_data, indent=3))

Result

That's it: The result is an interactive bundle diagram with all the dependencies between Java types of the application!

In the figure above, I've hovered over the class OwnerRepository and I can immediately see the types that this class depends on (red) and the types that depend on the class OwnerRepository (green).

Next: You can also query your software on different abstraction levels. As a proof of concept, I did that for dependencies between packages already:

Stay tuned for more Dependency Analysis in a follow-up post!