So, why would you want to have your source code in a graph database? Because this gives you a very powerful way to analyze your source code! JQAssistant creates a graph database with nodes and relations such as:
- TYPE declares METHOD
- TYPE declares FIELD
- TYPE extends TYPE
- TYPE implements TYPE
- METHOD invokes METHOD
- METHOD throws METHOD
- METHOD reads FIELD
- METHOD writes FIELD
- METHOD annotated_by METHOD
- ...
- It can be used to ensure architecture guidelines, such as "frontend classes may only call service layer classes, but not backend classes".
- Ensure naming guidelines, "e.g. all Service classes must end in *Service or *ServiceImpl and frontend classes in a certain package must only be *Model or *Controller".
- All unit tests must call an Assert.* method - otherwise the test does not test anything.
- What are the most complex methods in my code that are not called from unit tests?
- Analyze properties of your source, e.g. number of classes, methods etc.
Important to note is that there is a Maven plugin to execute JQAssistant during builds. This allows you to run JQAssistant queries during the build and for example let's you fail the built if certain architecture guidelines are not met.
We are doing this for QualityCheck already, even though we have only implemented a very simple check so far. This checks verifies that all unit tests match our name pattern
".*(Test|Test_.*)"
. Here is the relevant JQAssistant configuration my-rules.xml.
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="my-rules:TestClassName"> <requiresConcept refId="junit4:TestClass" /> <description>All JUnit test classes must have a name with suffix "Test".</description> <cypher><![CDATA[ MATCH (t:Junit4:Test:Class) WHERE NOT t.name =~ ".*(Test|Test_.*)" RETURN t AS InvalidTestClass ]]></cypher> </constraint> <group id="default"> <includeConstraint refId="my-rules:TestClassName" /> </group> </jqa:jqassistant-rules>JQAssistant is even more useful for applications using QualityCheck! For example, QualityCheck encourages you to use the methods from Check.*, such as
Check.notNull
in all public methods of your classes and annotate methods usings @ArgumentsChecked
.
So, you could use JQAssistant> to find methods, annotated with @ArgumentsChecked
, but who do not call any Check.* methods:
-- -- Find all methods having @ArgumentsChecked but not calling Check.* methods -- MATCH (checkType:Type), (type:Type)-[:DECLARES]->(method:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(checkAnnotationType:Type) WHERE checkType.fqn = 'net.sf.qualitycheck.Check' AND method.visibility = 'public' AND checkAnnotationType.fqn='net.sf.qualitycheck.ArgumentsChecked' AND NOT type.fqn =~ ".*(Test|Test_.*)" AND NOT (method:Method)-[:INVOKES]->(:Method)<-[:DECLARES]-(checkType:Type) RETURN method.signature AS Method, type.fqn as Type;Additionally, the other way round should be checked, i.e. find all methods who call Check.* but that do not have the annotation.
-- -- Find methods calling Check.* but not having @ArgumentsChecked in all non-test classes -- MATCH (checkType:Type)-[:DECLARES]->(checkMethod:Method), (type:Type)-[:DECLARES]->(method:Method)-[:INVOKES]->(checkMethod:Method), (checkAnnotationType:Type) WHERE checkType.fqn = 'net.sf.qualitycheck.Check' AND method.visibility = 'public' AND checkAnnotationType.fqn='net.sf.qualitycheck.ArgumentsChecked' AND NOT type.fqn =~ ".*(Test|Test_.*)" AND NOT (method:Method)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(checkAnnotationType:Type) RETURN checkMethod.name AS CHECK_METHOD_CALLED, method.signature AS Method, type.fqn as Type;As a software architect, you should now create a third rule, which indicates which methods must use check and must have the annotation, i.e. all public methods in classes call "*ServiceImpl".
I hope this was a small introduction and gave you a small impression of the power of this tool. I would be happy to get some feedback on this and also share some questions that arise for the future of this.
- Is there C++ support planned? Is someone working on a C++ plugin to parse C++ code?
- How does this perform with big systems? Does anyone have used this on a larger system?
- Is there a Sonar Qube integration? Can I show failed checks as violations or in charts in Sonar?
- What are your use cases and queries?