Werkzeuge zur Erhaltung der Softwarearchitektur – jQAssistant – Teil 13


jQAssistant

In den bisherigen Posts haben ich euch Werkzeuge mit feste Prüfregeln vorgestellt. Mit den nun folgenden sehr interessanten Werkzeug wird die Codestruktur in einer Datenbank abgelegt und auf dieser über eine Abfragesprache Analysen definiert. Beispiel für solche ABfragen sind:

  • Wo fehlt eine Annotation?
  • Analyse zyklischer Abhängigkeiten
  • Welche Teile der Software werden nicht genutzt?
  • Liegen alle Facades in einem Packge „*.facade“?

jQAssistant

Die Software jQAssistant übernimmt die Struktur des compilierten Codes in die Graphdatenbank Neo4j. In dieser Datenbank kann mit der Abfragesprache Cypher die Struktur analysiert und auf diesem Wege die Einhaltung der Architektur geprüft werden. Der Ablauf für die Nutzung ist wie folgt

  • jQAssistant-Server mit DB Neo4j scannt die Artefakte, die ausgewertet werden sollen
  • manuelle, explorative Analyse der Daten
  • Automatische Analyse von gruppierten Regeln, die aus der manuellen Analyse entstanden sind (Regeln in XML oder AsciiDoc)

Welche Abfragen auf der Datenbank möglich sind, wird über Konzepte bestimmt, die als Plugins für jQAssistant bereitgestellt werden. Her gibt es Plugins für Java, JEE, JPA aber auch eine Vielzahl anderer z.B. für die Ergebnisse der statischen Codeanalyse, um diese zu visualisieren. Auch ist es möglich weitere Konzepte selbst zu erstellen, um eigene Architekturmerkmale zu prüfen.

Die erzeugten Regeln können einen Schweregrad (Severity) zugeordnet werden, über den ein geordneter Abbau technischer Schulden gesteuert werden kann.

Scannen von Artefakten

Der erste Schritt für die Nutzung von jQAssistant ist es, die Abhängigkeiten zwischen Artefakten zu erzeugen (natürlich nach dem Download und dem Entpacken). Im Anschluss wird der Server gestartet und das analysieren der Daten kann beginnen. Ein Skript könnte wie folgt aussehen

echo Daten einlesen
/opt/jqassistant/bin/jqassistant.sh scan -s /opt/jqassistant/jqassistant/store 
                      -reportDirectory /opt/jqassistant/jqassistant/report 
                      -f /opt/jqassistant/source/

echo erzeuge Pfade
/opt/jqassistant/bin/jqassistant.sh analyze -s /opt/jqassistant/jqassistant/store 
                      -reportDirectory /opt/jqassistant/jqassistant/report 
                      -concepts classpath:Resolve

echo starte Server
/opt/jqassistant/bin/jqassistant.sh server -s /opt/jqassistant/jqassistant/store 
                 -reportDirectory /opt/jqassistant/jqassistant/report 
                 -serverAddress 0.0.0.0

Nach dem Einlesen der Daten kann über das Web-UI (die URL wird nach dem Start in der Shell ausgegeben) die erste Abfrage erstellt werden.

Eine tabellarische Darstellung der Ergebnisse ist ebenfalls möglich.

Inhalte der Datenbank

Die Inhalte der Datenbank hängen von den Konzepten ab. Ein Schema wie bei RDMS gibt es bei Graphdatenbanken nicht. Nicht alle Konzepte sind derzeit so gut dokumentiert, dass aus der Dokumentation die Abfragemöglichkeiten ersichtlich sind. Grundsätzlich besteht eine Graphdatenbank erwarteter Weise aus Knoten (hier mit Properties[Key/value] z.B. für Namen, Label) und Beziehungen (mit Properties, Richtung). Die grafische Anfrage GUI liefert uns alle Informationen, die wir zum arbeiten benötigen. Mit Hilfe des Punktes „Database Information“ werden alle aktuell verwendeten Knotentypen und Beziehungstypen angezeigt

Beispiel

Es folgt nun ein kleines Beispiel, um euch eine Idee des Arbeitens zu vermitteln.

MATCH
 (c:Class)-[:DECLARES]->(m:Method)
WHERE c.fqn =~ 'de.schoeso.*.*Facade'
  AND NOT(m.name =~ '__cobertura.*')   AND NOT(m.name = '')
  AND m.visibility = 'public' AND (m.static IS NULL OR m.static = false)
  AND NOT((m)-[:ANNOTATED_BY] -> 
       ()-[:OF_TYPE] ->(:Type{name:'TransactionAttribute'}))
RETURN c.name, m.name, m.static

Wie man in der Abfragesprache schon sieht sind die Kanten gerichtet. Es werden Klassen (Knoten vom Typ Class) gesucht, die eine Methode besitzen. Die „WHERE“ Bedingung schränkt dies weiter ein. Es sollen nur Klassen mit einem speziellen Namen (und damit auch Package) betrachtet werden. Cobertura Klassen und Konstruktoren sollen nicht betrachtet werden. Die betrachteten Methoden sollen den Sichtbarkeitsmodifikator „public“ besitzen und nicht statisch sein. Abschließend werden jene Methoden gesucht, die keine Annotation vom Typ „TransactionAttribute“ besitzen.

Das Ergebnis wird hier als Liste dargestellt, kann aber immer auch als interaktiver Graph ausgegeben werden.

Abfragen

Wie wir gesehen haben, ist jQAssistant ein sehr flexibles Werkzeug. Ich habe hier ein paar Abfragen für unseren Kontext zusammengestellt.

  • Allgemein
    • Kein Java-Paket hat mehr als n Typen
    • Keine Klasse hat mehr als n Methoden
    • Test-Klassen-/Methodenbezeichnungen beginnen mit Test
    • Sehr große Ableitungshierarchie sollen nicht existieren
  • Entwurfsmuster
    • Klassen mit mehr als n Konstruktoren ermitteln, um ggf. eine Factory zu definieren
  • Ordnung insb. Schichtenbildung
    • Keine zyklischen Artefakte, Packages, (Klassen) mit Prüfung direkter / indirekter Abhängigkeiten
    • Fachliche Schichten bei „guten“ Paketnamen; gibt es ein fachliches Schema für die Pakete (z.B. Abrechnung, Stammdaten und kein technisches z.B. Entity, Facade), können fachliche Abhängigeiten sehr gut analysiert werden
    • Vermeidung von „Split Packages“ (Pakete, die sich auf verschiedene Artefakte verteilen, um z.B. „protected“ Methoden eine Lib nutzen zu können; diese sind im Java 9 Modulsystem nicht mehr erlaubt
  • EJB
    • Jede Facade implementiert ihr Interface
    • Facade implementieren Interfaces mit dem Namen der Facade als Präfix
    • Public Facade Methoden finden sich im Interface
    • Public Facade Methoden haben eine Annotation vom Typ @TransactionAttribute
    • „Find…“ Methoden haben ein Annotation @TransactionAttribute.Supports
  • Web
    • Im Web sind Injection von EJB immer local
  • EAR
    • Welche Methoden aus Interfaces werden nicht mehr genutzt und können somit bereinigt / gelöscht werden

Integration

  • Eigene GUI
  • Maven-Build mit Regelwerk (Abbruch nach Schweregrad der verletzten Regel)

Regeln

Regeln werden innerhalb des Maven Plugins angegeben und ausgeführt. Wird mindestens ein Ergebnis gefunden ist die Regel nicht erfüllt.

<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0">
    <constraint id="schoeso-rules:testClassPackageCount" severity="critical">
        <description>Kein Java-Paket hat mehr als 20 Typen</description>
        <cypher><![CDATA[
           MATCH (p:Package)-[CONTAINS]->(t:Type)
           WHERE p.fqn =~ 'de\\.schoeso\\..*'
             AND t.fqn =~ 'de\\.schoeso.[^\\$]*'
            WITH p, count(t) AS typeCount
           WHERE typeCount > 20
          RETURN p.fqn
        ]]></cypher>
    </constraint>
    <!-- auch hier kann severity="critical" gesetzt werden -->
    <group id="schoesoDefault">
        <includeConstraint refId="schoeso-rules:testClassPackageCount" />
        <includeConstraint refId="schoeso-rules:testTestClassName" />
    </group>
</jqa:jqassistant-rules>

Du hast Fragen oder Anmerkungen? Kontakt: arndt@schoenb.de