Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. 

Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. Individuelle Softwarelösungen. Digitale Transformation. Agile Softwareentwicklung. 

10 min

Codeanalyse mit jQAssistant (Teil 1)

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?

Es konzentriert sich auf drei Hauptanwendungsfälle:

  1. 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.
  2. 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.
  3. 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:classesWithHighestNumberOfMethods 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.