Die Codestruktur eines Softwareprojekts ist ein wichtiger Faktor für die Wartbarkeit und Erweiterbarkeit.
Wie kann man nun verhindern, dass die Struktur im Laufe der Zeit der Entropie zum Opfer fällt?
Eine mögliche Antwort ist das Open-Source-Tool jQAssistant. Im ersten Teil dieser Blogreihe lernt ihr, wie ihr jQAssistant in euer Java-Projekt integriert und erste Regeln definiert.
jQAssistant in die eigene Codebasis integrieren
Motivation
Am Anfang eines Projekts startet man meist mit der Idee einer klaren Codestruktur. Man überlegt sich, welche Klassen und Funktionalitäten zusammengehören und ordnet diese in Packages und Module ein.
Mit der Zeit wächst nun die Codebasis. Neue Funktionen werden eingebaut, alte Funktion abgeändert, Mitarbeiter im Projekt kommen und gehen.
Die anfangs erdachte Struktur der Packages und Module wurde immer weiter aufgeweicht und neue Funktionen werden einfach irgendwo in der Struktur abgelegt, da sich kein richtiger Ort finden lässt.
Alle Entwickler merken, dass mit der Struktur der Codebasis etwas nicht stimmt und man da mal etwas machen sollte.
Nur leider kann man die Probleme nicht richtig greifen und es fehlt der konkrete Ansatz für die Neustrukturierung.
In dieser Blogreihe möchte ich aufzeigen, wie jQAssistant dabei helfen kann, die aktuelle Struktur eurer Codebasis zu analysieren, visualisieren und zu überwachen. Bei neuen Projekten kann jQAssistant von Anfang an dafür sorgen, dass sich der Code jedes neuen Features an die erdachte Struktur hält und es nicht zu einer Entropie der Codebasis kommt. Bei bestehenden Projekten kann es dabei helfen, die aktuelle Struktur zu verstehen und etwaige Refactorings zu planen und zu überwachen.
Übersicht der Blogreihe
Im ersten Teil dieser Blogreihe werde ich zeigen, wie ihr jQAssistant in ein Java Projekt mit Maven einbaut und erste Regeln definiert.
Der zweite Teil wird sich intensiv mit der Analyse der Codestruktur und der Einführung verschiedener Metriken beschäftigen.
Im dritten Teil verlassen wir dann die eigene Codebasis und bauen uns mithilfe von jQAssistant ein Tool zur Analyse der Struktur von fremden Projekten.
So ist der aktuelle Plan, mal schauen, wo uns die Reise hinführt. 🙂
Was ist jQAssistant?
jQAssistant ist ein Open-Source-Tool, das dazu dient, die Qualität von Software-Systemen zu analysieren und zu kontrollieren.
Es konzentriert sich auf drei Hauptanwendungsfälle:
- Software-Analyse: Mit jQAssistant könnt ihr Software-Systeme analysieren, um Einblicke in die Struktur zu gewinnen. Es scannt Ressourcen wie Quellcode, XML-Konfigurationsdateien, Maven-Build- und Abhängigkeitsinformationen oder Dokumentationsartefakte und speichert diese in einer Neo4j-Graphdatenbank. Diese Datenbank dient als Grundlage für weitere Analysen.
- Verifizierung der Implementierung: jQAssistant überprüft die korrekte Implementierung von Design und Architektur. Dabei werden vordefinierte oder benutzerdefinierte Regeln verwendet, um Qualitätsziele zu erreichen.
- Lebendige Dokumentation: Durch eine automatische Generierung von Dokumentation auf Basis des Codes wird die Lücke zwischen Implementierung und Dokumentation verkleinert.
jQAssistant bietet in erster Linie Unterstützung für Java Anwendungen mit Maven. Durch seine plugin-basierte Architektur kann es jedoch auch für andere Technologien erweitert werden.
Im Laufe dieser Serie werden wir einige Plug-ins selbst verwenden. Eine Liste mit offiziellen Plug-ins findet ihr hier . Es gibt jedoch noch viele weitere Plug-ins, die von der Community entwickelt wurden.
Weitere Informationen findet ihr in der Dokumentation und in einem der vielen Tutorials auf GitHub.
jQAssistant in die eigene Codebasis einbauen
Ein lauffähiges Beispiel für die Integration von jQAssistant in ein Maven Projekt findet ihr auf GitHub .
Die Beispielanwendung im verlinkten Repository besteht aus den drei Säulen movie
, schedule
und theater
. Jeder der drei Säulen ist intern wiederum nach dem Schichtenmodell aufgebaut. Die Anwendung dient als Testobjekt für die verschiedenen jQAssistant-Features.
Wer schon mal mit jQAssistant in Berührung gekommen ist, wird merken, dass sich mit dem Upgrade auf Version 2.x vieles geändert hat. Zum Zeitpunkt des Schreibens ist die offizielle Dokumentation noch nicht überall auf dem neusten Stand.
Wer ein älteres Projekt auf jQAssistant 2.x upgraden möchte, findet in diesem Migration-Guide weitere Informationen.
Im Folgenden beziehe ich mich auf die Version 2.1.0 von jQAssistant.
Integration in Maven-Projekt
Um jQAssistant in ein Java-Projekt zu integrieren, muss das jqassistant-maven-plugin
in die pom.xml
eingebunden werden.
<properties>
<jqassistant.version>2.1.0</jqassistant.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.buschmais.jqassistant</groupId>
<artifactId>jqassistant-maven-plugin</artifactId>
<version>${jqassistant.version}</version>
<executions>
<execution>
<goals>
<goal>scan</goal>
<goal>analyze</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Mit dem jqassistant:scan
-Goal wird die Codebasis gescannt und die Informationen in einer Neo4j-Datenbank gespeichert.
Das jqassistant:analyze
-Goal führt die definierten Regeln aus und überprüft die Codebasis.
Nach der Analyse kann mit dem jqassistant:report
-Goal ein Report generiert werden, dazu später mehr.
Diese Goals werden auch automatisch in der verify
-Phase ausgeführt.
Mit dem jqassistant:server
-Goal kann zusätzlich ein lokaler Neo4j-Server gestartet werden, um die Datenbank zu visualisieren und zu durchsuchen. Hierbei muss jedoch beachtet werden, dass zuerst jqassistant:scan
ausgeführt wird, da die Datenbank sonst leer ist.
Konfiguration für Java 16 und höher
Seit Java 16 und der Einführung von JEP 396 ist es notwendig folgende Umgebungsvariablen zu setzten, damit jQAssistant funktioniert:
Linux
export MAVEN_OPTS=--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED
Windows
set MAVEN_OPTS=--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED
Alternativ können diese Einstellung auch in der .mvn/jvm.config
Datei im Projektverzeichnis gesetzt werden.
.mvn/jvm.config
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED
Erstellen der jQAssistant Konfigurationsdatei
jQAssistant kann über verschiedene Wege konfiguriert werden. Die einfachste Möglichkeit ist die Verwendung einer jqassistant.yml
Konfigurationsdatei, welche im Projektverzeichnis abgelegt wird.
jqassistant.yml
jqassistant:
Fürs Erste reicht diese nahezu leere Datei aus. Eine vollständige Dokumentation der möglichen Konfigurationsoptionen findet ihr hier .
Erste Regel definieren
Regeln oder „Rules“ bezeichnen alle Abfragen, Analysen und Überprüfungen, die jQAssistant auf den erzeugten Daten in der Neo4j-Datenbank ausführt.
Hierbei gibt es zwei Arten von Regeln:
- Concepts: Dienen dazu, die Daten über die Struktur des Softwareprojekts abzufragen, zu analysieren und um neuen Informationen zu ergänzen. Abfragen in einer Concept-Regel müssen ein Ergebnis zurückgeben, um keine Warnung zu erzeugen.
- Constraints: Dienen zur Überprüfung der Einhaltung von Konzepten und Bedingungen. Wenn die Abfrage einer Constraint-Regel ein Ergebnis zurückgibt, wird dies als Verletzung der Regel gewertet und erzeugt einen Fehler.
Vor der Version 2.x.x von jQAssistant wurden Regeln Vorzugweise in Asciidoc-Dateien definiert. Mit dem Versionswechsel wurde die standardmäßige Unterstützung von Asciidoc Regeln jedoch entfernt. Durch die Einbindung des jqassistant-asciidoc-report-plugin
ist die Nutzung von Asciidoc-Regeln zwar weiterhin möglich, laut dem offiziellen Migration-Guide wird das Plug-in jedoch nicht weiter entwickelt und es wird empfohlen, Regeln in XML zu definieren.
Standardmäßig sucht jQAssistant nach projektspezifischen Regeln im Ordner jqassistant/
im Projektverzeichnis.
Im Folgenden seht ihr ein Beispiel für eine einfache Regel:
jqassistant/my-rules.xml
<jqassistant-rules
xmlns="http://schema.jqassistant.org/rule/v1.10"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schema.jqassistant.org/rule/v1.10 https://schema.jqassistant.org/rule/jqassistant-rule-v1.10.xsd">
<group id="default">
<includeConcept refId="my-concept:*"/>
</group>
<concept id="my-concept:classesWithHighestNumberOfMethods">
<description>A list of classes with the highest number of methods</description>
<cypher><![CDATA[
MATCH
(class:Class)-[:DECLARES]->(method:Method)
RETURN
class.fqn AS Class , count(method) AS Methods
ORDER BY
Methods DESC
LIMIT
20
]]></cypher>
</concept>
</jqassistant-rules>
Hinweis: Zum Zeitpunkt der Veröffentlichung dieses Blogeintrags gibt es noch kein aktualisiertes xsd-Schema für die Version 2.x.x von jQAssistant. Das oben gezeigte Schema ist das aktuellste, das ich finden konnte. Die Funktionalität wird dadurch jedoch nicht beeinträchtigt.
In der oben gezeigten Regel wird eine Gruppe so wie eine Concept-Regel definiert. Gruppen dienen dazu, mehrere Regeln oder auch andere Gruppen zusammenzufassen. In unserem Fall erzeugen wir eine Gruppe mit der id default
, welche alle Regeln enthält, die mit dem Präfix my-concept:
beginnen. jqAssistant führt standardmäßig die Gruppe default
aus, wenn keine andere Gruppe in der Konfiguration angegeben ist.
Die Concept-Regel my-concept:classes
gibt eine absteigend sortierte Liste von Klassen und der Anzahl ihrer Methoden zurück. Regeln werden gängigerweise in Cypher definiert, einer Abfragesprache der Neo4j Graphdatenbank. Neben Cypher können Regeln aber auch in Skriptsprachen wie JavaScript, Ruby oder Groovy definiert werden.
Die Grundlagen von Cypher sind schnell erlernt. Eine gute Einführung findet ihr auf der offiziellen Neo4j Webseite .
Nachdem wir nun unsere erste Regel definiert haben, können wir sie mit mvn install
ausführen. Nach erfolgreichem Build erscheint im target
-Ordner des Projekts unter target/jqassistant/jqassistant-report.xml
das Ergebnis der Regel.
Codebasis visualisieren und Cypher-Queries ausführen
Wie bereits oben erwähnt, kann jQAssistant mit mvn jqassistant:server
einen lokalen Neo4j-Server starten. Unter http://localhost:7474
könnt ihr die Daten in der Graphdatenbank durchstöbern und Cypher-Queries ausführen.
In der neo4j Weboberfläche können die erstellten Cypher-Queries ausprobiert und verfeinert werden, bevor ihr daraus jQAssistant-Regeln erstellt.
Erzeugen eines Reports im Asciidoctor
Damit die Ergebnisse der erstellten Regeln auch ordentlich dargestellt werden, empfiehlt sich die Verwendung des asciidoctor-maven-plugin
Plug-ins (GitHub ) in Verbindung mit der jqassistant-asciidoctorj-extensions
Erweiterung (GitHub ).
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<attributes>
<jqassistant-report-path>
${project.build.directory}/jqassistant/jqassistant-report.xml
</jqassistant-report-path>
</attributes>
</configuration>
<executions>
<execution>
<id>output-html</id>
<phase>verify</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jqassistant.tooling.asciidoctorj</groupId>
<artifactId>jqassistant-asciidoctorj-extensions</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Das asciidoctor-maven-plugin
kann die Ergebnisse der jQAssistant-Regeln in die normale Dokumentation des Projekts einbinden. Hierzu erzeugen wir im Projektverzeichnis den Ordner src/docs/asciidoc
und legen dort eine neue Asciidoc-Datei an.
src/docs/asciidoc/documentation.adoc
= My Project Documentation
== jQAssistant
include::jQAssistant:Rules[]
Bei einem erneuten Build des Projekts mit mvn install
wird nun im target
-Ordner unter target/generated-docs/index.html
die Dokumentation des Projekts mit den Ergebnissen der jQAssistant-Regeln abgelegt.
Unten seht ihr die erzeugte Dokumentation für das verlinkte Beispielprojekt in GitHub.
So können mit jQAssistant erstellte Auswertungen in die reguläre Dokumentation eines Projekts integriert werden
Fazit und Ausblick
Im ersten Teil dieser Blogreihe haben wir jQAssistant in ein Java-Projekt integriert, eine erste Regel definiert und die Ergebnisse in einem Report dargestellt. Der Grundstein für die tiefergehende Analyse und Überwachung der Struktur unseres Projekts ist gelegt.
Im nächsten Teil werden wir uns intensiver mit der Erstellung von Regeln und der Überwachung der Codequalität und Struktur beschäftigen.