Werkzeuge zur Architektur- und Code-Validierung: jqAssistant

Es existieren unterschiedliche Werkzeuge, um Architekturen und Softwareprojekt-Strukturen zu analysieren und zu validieren. Bei jqAssistant handelt es sich um ein neues interessantes Tool für diese Anwendungsfälle, das im Hintergrund mit der bekannten Graphendatenbank Neo4j und ihrer Abfragesprache Cypher arbeitet.

Es analysiert gegebene Projekte und Artefakte, speichert deren Metadaten und Relationen in der Graphendatenbank und eine Vielzahl unterschiedlicher Plugins reichern diese Datenbank dann um weitere Informationen an, die als Grundlage späterer Analysen und Validierungen dienen können.

Download und Installation

jqAssistant lässt sich entweder über die Kommandozeile bedienen oder über das Build-Tool Maven als Plugin in ein Projekt integrieren. Wir verwenden für die folgenden Beispiele das Kommandozeilen-Tool, das auf der entsprechenden Website als Jar-Datei zum Herunterladen verfügbar ist.

Nun steht uns für Linux-basierte Systeme mit bin/jqassistant.sh bzw. für Windows-Systeme mit bin/jqassistant.cmd alles zur Verfügung, was wir benötigen, um bestehende Strukturen analysieren und validieren zu können.

Graphendatenbank erzeugen

Mittels jqassistant.sh scan -f Verzeichnis können wir nun alle Jar/Class-Files in einem Verzeichnis analysieren lassen und die Ergebnisse in eine Graphendatenbank schreiben, die uns dann für die nächsten Schritte als Ausgangspunkt dient.

Das Ergebnis kann so aussehen:

% bin/jqassistant.sh scan -f some-app.jar
2018-01-03 08:28:45.722 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML].
2018-01-03 08:28:46.073 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store'
2018-01-03 08:28:49.268 [main] INFO AbstractContainerScannerPlugin - Entering /data/project/some-app/target/some-app-2.7.0-SNAPSHOT.jar
2018-01-03 08:28:56.848 [main] INFO AbstractContainerScannerPlugin - Leaving /data/project/some-app/target/some-app-2.7.0-SNAPSHOT.jar (1791 entries, 7578 ms)

System im Browser erforschen

Um uns einen Eindruck vom zu analysierenden bzw. zu validierenden System zu verschaffen, können wir den Neo4j-Server samt GUI mit dem folgenden Befehl starten, sodass wir anschließend im Browser Abfragen auf die Datenbank durchführen können:

% bin/jqassistant.sh server
2018-01-03 08:35:14.973 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML].
2018-01-03 08:35:15.338 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store/'
[..]
2018-01-03 08:35:19.232+0100 INFO [o.n.s.CommunityNeoServer] Remote interface ready and available at http://localhost:7474/
2018-01-03 08:35:19.232 [main] INFO ServerTask - Running server
2018-01-03 08:35:19.232 [main] INFO ServerTask - Press <Enter> to finish.

Daraufhin können wir unter der Adresse http://localhost:7474/ unsere Datenbank im Browser durchsuchen. Wollen wir beispielsweise alle Klassen auffinden, die sich im Package net.seibertmedia befinden, bewerkstelligen wir das mit der folgenden Abfrage:

MATCH (p:Package)-[:CONTAINS*]->(class:Class)
WHERE p.fqn = "net.seibertmedia"
RETURN p, class

Das Ganze stellt sich im Neo4j-Browser dann wie folgt dar:

Klassen nach Package strukturiert im Neo4j Browser

Klassen nach Package strukturiert im Neo4j-Browser

Systeme validieren

Im letzten Abschnitt wollen wir näher auf die Möglichkeiten, mit jqAssistant Systeme zu validieren, eingehen. Nehmen wir für dieses beispielhafte Szenario einmal an, wir möchten verhindern, dass Klassen, die sich in einem vorgegebenen Package befinden, Methoden mit mehr als fünf Parametern definieren dürfen.

Zunächst erstellen wir eine Abfrage mittels Cypher, die solche Methoden auffindet:

MATCH (c:Class)-[:DECLARES]->(m:Method)-[p:HAS]->(:Parameter)
WHERE c.fqn STARTS WITH "net.seibertmedia"
WITH c.fqn AS class, m.name AS methodName, COUNT(p) AS params
WHERE params > 5
RETURN class+"#"+methodName

Wir erstellen nun im Projektverzeichnis ein Unterverzeichnis jqassistant/rules und speichern dort die folgende XML-Datei unter dem Namen custom-rule.xml ab:

<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">

<constraint id="seibert:DemoConstraint">
 <description>Classes in the package 'net.seibertmedia' must not declare methods with more than 5 parameters.</description>
 <cypher><![CDATA[
 MATCH (c:Class)-[:DECLARES]->(m:Method)-[p:HAS]->(:Parameter)
 WHERE c.fqn STARTS WITH "net.seibertmedia"
 WITH c.fqn AS class, m.name AS methodName, COUNT(p) AS params
 WHERE params > 5
 RETURN class+"#"+methodName, params
 ]]></cypher>
 </constraint>

<group id="default">
 <includeConstraint refId="seibert:DemoConstraint" />
 </group>

</jqa:jqassistant-rules>

Jetzt können wir die Validierung mittels bin/jqassistant.sh analyze starten – und wie zu erwarten war, schlägt die Validierung fehl und meldet uns drei Methoden, die mehr als fünf Parameter deklarieren:

% bin/jqassistant.sh analyze
2018-01-03 09:23:25.774 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML].
2018-01-03 09:23:26.128 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store/'
2018-01-03 09:23:28.998 [main] INFO AbstractAnalyzeTask - Executing analysis.
2018-01-03 09:23:29.010 [main] INFO AbstractAnalyzeTask - Reading rules from directory /data/project/some-app/jqassistant/rules
2018-01-03 09:23:29.107 [main] INFO AbstractAnalyzeTask - Executing group 'default'
2018-01-03 09:23:29.110 [main] INFO AbstractAnalyzeTask - Validating constraint 'seibert:DemoConstraint' with severity: 'MAJOR'.
2018-01-03 09:23:29.888 [main] INFO AbstractAnalyzeTask - Verifying results: failOnSeverity=MAJOR, warnOnSeverity=MINOR
2018-01-03 09:23:29.888 [main] ERROR AbstractAnalyzeTask - --[ Constraint Violation ]-----------------------------------------
2018-01-03 09:23:29.888 [main] ERROR AbstractAnalyzeTask - Constraint: seibert:DemoConstraint
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - Severity: MAJOR
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - Classes in the package 'net.seibertmedia' must not declare methods with more than 5 parameters.
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.plugins.util.VelocityMacroHelper#renderConfluenceMacro, params=8
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.plugins.theme.configuration.boundary.ThemeConfigurationWebservice#<init>, params=6
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.aop.advice.LoggingAspect#logAdviceWithUser, params=6
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - -------------------------------------------------------------------
2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask -

2018-01-03 09:23:29.954 [main] ERROR Main - -> Failed rules detected: 0 concepts, 1 constraints

Für weitere Informationen und Metriken empfiehlt sich die Lektüre des folgenden Blog-Artikels und der ausführlichen technischen Dokumentation des Projekts.

Weiterführende Infos

Architekturregeln mit Java und ArchUnit sicherstellen
Analyse einer Java-Anwendung mit Java Mission Control und Flight Recorder
Kurzeinführung in das Testen von Web-Services mit Karate und JUnit
Was agile Software-Projekte dem Kunden bringen

Artikel teilen:Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someonePrint this page