JHipster ist eine offenen Entwicklugsplatform zur Entwicklung von modernen, Spring Boot basierten Webanwendungen mit Angular, React oder Vue Frontend zu entwicklen. Dieser Artikel beschreibt, wie JHipster helfen kann sich bei der Entwicklung auf die Kernfunktionen zu fokussieren ohne sich um das zeitraubende Setup von z.B. Datenbanken, Buildsystem oder Deployment in der Cloud zu kümmern.

Der Scrum Master Samu hat ein Problem. Er kann nicht noch mehr Funktionen aus dem unternehmensweiten Ticketsystem herausholen. Daher spielt er mit dem Gedanken eine Webanwendung zu schreiben, die sich mit dem bestehenden Ticketsystem integriert und er dort spezielle Anforderungen und Workflows implementieren kann. Leider liegen seine Tage als Entwickler etwas weiter zurück und er hat auch keine Zeit sich mit dem Schreiben von Boilerplate Code einer modernen Webanwendung zu befassen. Zum Glück erinnert er sich, dass die Java-Entwicklerin Jennifer in ihrer Freizeit im JHipster Projekt aktiv ist. In diesem Artikel begleiten wir Samu dabei, wie er die Möglichkeiten von JHipster erkundet, um einen ersten Prototypen zu bauen und diesen am Ende in der Cloud zu betreiben.

Was ist JHipster?

Samu schaut sich zunächst die Dokumentation von JHipster an [1]. JHipster behauptet von sich selbst ein Framework zu sein, um Spring Boot-basierte Webanwendungen und Microservices mit Angular, React oder Vue Frontend zu generieren, zu entwickeln und zu betreiben[2].

JHipster kombiniert unterschiedliche Technologien und Frameworks, konfiguriert diese nach aktuellen Best Practices und stellt sicher, dass die verwendeten Technologien reibungslos miteinander funktionieren. Da Samu für einen Prototypen keine Zeit hat sich beispielsweise in die Besonderheiten von Spring Security einzulesen oder in die Konfiguration von Angular und Webpack einzuarbeiten, gibt er JHipster eine Chance. Er wird mit JHipster die Anwendungen bootstrappen und das Datenmodell erzeugen, damit er sich auf das Implementieren der Businesslogik konzentrieren kann.

JHipster hilft nicht nur Anfängern, sondern auch erfahrenen Entwicklern schneller produktiv zu sein [3].

JHipster in Zahlen

Samu möchte auf einem aktiven und gut gepflegten Projekt aufbauen, damit er bei Problemen und Fragen schnell Hilfe findet und Fehler durch das Team zeitnah behoben werden können.

Er schaut sich die Historie des Projekts an. JHipster wurde im Jahr 2013 gestartet, seitdem gab es sechs Major Releases. Die aktuelle Version ist 6.1.0 (Juni 2019). Das Projekt ist sehr aktiv [4] und wird von einigen Firmen finanziell gesponsert. Im letzten Monat wurden fast 100 Pull Requests gemerged und mehr als 70 Issues geschlossen (Abbildung 1). Das Kernteam besteht aus 31 Mitgliedern, darunter auch zwei Java Champions und insgesamt 500 Contributern. Einige Teile, wie zum Beispiel der Kubernetes Support, werden direkt von Google entwickelt. Unter den Nutzern von JHipster [5] befinden sich neben Google, HBO und Pivotal auch Firmen wie Siemens und Bosch. JHipster hat fast 14.000 Stars auf Github und nach eigener Angabe mehr als 25000 Downloads pro Woche.

Samu ist überzeugt, dass ihm bei Problemen die Community hilfreich zur Seite stehen kann. Auch die Aktivität des Teams stimmt Samu positiv, dass Fehler schnell behoben werden.

Github Pulse
Abbildung 1. Github Pulse Mai/Juni 2019

Installation

JHipster bietet verschiedene Installationsarten an [6]. Die Dokumentation empfiehlt die lokale Installation, dennoch möchte Samu von Jennifer gerne mehr erfahren. Insbesondere JHipster Online klingt sehr spannend, daher schreibt er Jennifer eine Chat-Nachricht wie er am besten starten sollte.

JHipster Online

Jennifer erklärt ihm, dass JHipster Online [7] selbst mit JHipster entwickelt wird. Mit JHipster Online kann man eine JHipster Anwendung konfigurieren und als Zip Archiv herunterladen (Abbildung 2). Wenn man seinen GitHub oder GitLab Account mit JHipster Online verbindet, dann können die Projekte direkt in einem Git Repository generiert werden. Weiterhin kann man verschiedene Entitätsmodelle verwalten und editieren und diese direkt in eine generierte Anwendung importieren. JHipster Online erzeugt dabei einen Pull Request auf dem Projekt, sodass man als Entwickler alle Änderungen noch reviewen kann und evtl. eingerichtete CI Pipelines die Änderungen bauen und testen können. Demnach ist es sehr einfach verschiedene Anwendungskonfigurationen und Datenmodelle zu testen.

Allerdings, so Jennifer, sollte man JHipster ebenfalls lokal installieren, da einige Funktionen wie beispielsweise Deployments bisher noch nicht von JHipster Online unterstützt werden. Jennifer gibt Samu noch den Tipp Docker und Docker Compose zu installieren, damit er die erzeugten Compose Skripte für Datenbanken verwenden kann und diese nicht lokal auf seinem System installieren muss.

JHipster Online
Abbildung 2. Anwendungskonfiguration in JHipster Online

Module und Blueprints

Samu ist besorgt, dass JHipster es ihm schwer macht, Konfigurationen zu ändern und Erweiterungen nicht einfach möglich sind. Jennifer kann ihn beruhigen. Da JHipster im Kern eine Spring Boot Anwendung ist, können alle Konfigurationen durch modifizierte Versionen überschrieben oder erweitert werden. Außerdem versucht JHipster die einzelnen Technologien in der Standardkonfiguration zu verwenden und Modifikationen nur vorzunehmen, wenn diese unbedingt notwendig sind [8]. Dadurch sind Anpassungen einfach möglich. Im Zweifel genügt ein Blick in die Dokumentation des entsprechenden (Upstream) Projektes.

Falls Samu größere Anpassungen vornehmen möchte, kann er auch ein Modul [9] oder einen Blueprint [10] entwickeln und JHipser Online on premise installieren, sodass automatisch immer sein spezielles Modul zusätzlich bei der Generierung einer Anwendung verwendet wird.

Ein Modul hat Zugriff auf die Konfiguration der JHipster Anwendung und kann, wie zum Beispiel ein Subgenerator, Dateien anlegen. Über bestimmte Erweiterungspunkte (sog. needles) kann ein Modul bestehende Dateien erweitern oder modifizieren. Es können beispielsweise neue Menüpunkte oder weitere Maven/Gradle Dependencies eingefügt werden.

Im Unterschied zu einem Modul kann ein Blueprint existierende Dateien überschreiben oder löschen und eine eigene Menge von Dateien ausliefern. Damit ist es dem Autor eines Blueprints möglich, u.a. Spring Boot durch ein anderes Framework zu ersetzen, die Spring Security Konfiguration an die Bedürfnisse des eigenen Unternehmens anzupassen oder Java durch Kotlin zu ersetzen [11]. Module sind seit JHipster 3 verfügbar, Blueprints erst seit Version 5 (als Beta). Daher ist die Auswahl an Blueprints noch nicht sehr groß.

Seit Version 6 ist der offizielle Blueprint um Vue.js als Client-Side Framework verwenden zu können in Version 1.0.0 verfügbar [12].

Let’s get started

Samu befolgt die Installationsanleitung [6] und installiert ein JDK (11), NodeJS in der aktuellen LTS Version, Docker und Docker Compose. JHipster installiert er via NPM (Listing 1).

Listing 1. Installation von JHipster
npm install -g yo generator-jhipster

Mit Version 6 wurden weitere Installationsarten [13], wie Homebrew oder die JHipster Devbox [14], eine Vagrant-basierte virtualisierte Entwicklungsumgebung abgekündigt, da der Nutzen entweder gering oder der Wartungsaufwand zu hoch gewesen ist.

Da Samu von den Entwicklern seines Teams viel Positives über vue.js gehört hat entscheidet er sich den vue.js Blueprint zu benutzen [12]. Nach der Installation des Blueprints via npm install -g generator-jhipster-vuejs kann dieser beim Erstellen einer Anwendung verwendet werden.

Samu startet den Generator im Terminal durch Eingabe von jhipster --blueprint vuejs. Er muss eine Reihe von Fragen beantworten und entscheidet sich fast immer für den Vorgabewert Abbildung 3. Statt MySQL als Datenbank verwendet er PostgreSQL, da er sich damit besser auskennt. Er entscheidet sich für Gradle statt Maven, da er bereits einige Gradle Skripte geschrieben hat. Für den Anfang belässt er es im Bereich Testing bei der Standardvorgabe. Daher werden Unit- und (Spring)Integrationstests generiert für den Serverteil generiert. Für den Frontendteil werden im Standard Unit-Tests mit Jest erzeugt, sodass jede JHipster Anwendung eine sehr gute Bewertung bei der Analyse mit Sonar bekommt [15]. Bei Bedarf können sowohl E2E Tests via Protractor, Performance Tests mit Gatling und Behaviour-driven Tests mit Cucumber generiert werden [16].

Anwendungskonfiguration
Abbildung 3. Konfiguration der JHipster Anwendung

Nach der Generierung der App startet Samu die Anwendung mit ./gradlew. Nachdem der Java- und der Frontendbuild erfolgreich durchgelaufen sind, öffnet Samu die Anwendung (localhost:8080) und wird von der JHipster-Willkommensseite begrüßt (Abbildung 4). Da Samu bei seiner Arbeit als Scrum Master oft von den traumhaften Turnaroundzeiten von modernen Javascript Anwendungen gehört hat, probiert er das Ganze direkt aus. Er startet in einem Terminal das Javabackend mit gradlew und in einem weiteren Terminal das Frontend mit npm run start. Samu fügt ein div Tag in die Datei src/main/webapp/app/core/home/home.vue ein und nach kurzer Zeit sind seine Änderungen im Browser zu sehen. Durch die Integration der Spring Boot DevTools muss auch bei Änderungen am Javacode nicht die komplette Anwendung neugestartet werden.

Momentan läuft seine Anwendung noch im sogenannten Development Profil. Daher sind die Javascript- und CSS-Dateien noch nicht optimiert und als Datenbank wird H2 statt PostgreSQL verwendet. Er folgt den Anweisungen der Dokumentation und startet die Anwendung im Production Profil (Listing 2). JHipster erzeugt für alle externen Services (z.B. Datenbanken) passende Docker Compose Skripte, damit man als Entwickler die Datenbank nicht lokal installieren muss.

Listing 2. Starten der Anwendung im Production Profile
docker-compose -f src/main/docker/postgresql.yml up -d (1)
./gradlew -Pprod (2)
1 JHipster PostgreSQL Datenbank starten
2 Anwendung im sog. Production Profile starten

Samu meldet sich mit dem hinterlegten Standardlogin admin/admin an und findet neben einer Benutzerverwaltung, einer Übersicht der erzeugten Metriken (Antwortzeit, Cachestatistiken, JVM Metriken) auch die Möglichkeit, die Loglevel zur Laufzeit zu ändern. Die Sprachumschaltung zwischen seinen gewählten Sprachen klappt reibungslos. Samu hat mit wenigen Befehlen eine Webanwendung mit Benutzerverwaltung, Security, REST Api und einem modernen Frontend generiert.

Nun kann er mit dem Erstellen und Testen seines Datenmodells fortfahren.

Willkommensseite
Abbildung 4. JHipster Willkommensseite

Das Datenmodell

Samu möchte in einem ersten Schritt einen speziellen Workflow abbilden. Auch wenn alle Teams bereits relativ autonom arbeiten können, gibt es gewisse Abhängigkeiten zwischen einzelnen Aufgaben. Zum Beispiel kann das Team Einkaufswagen erst dann neue Produktinformationen verarbeiten, wenn das Team Produkt diese Daten auch bereitstellt. Diese Abhängigkeiten will Samu explizit darstellen und z.B. ein Ticket für das Einkaufswagenteam erstellen, wenn das Produktteam sein Ticket abgeschlossen hat.

Dazu hat er sich ein einfaches Modell überlegt (Abbildung 5). Jedes Team hat einen Workspace, der mehrere Listen mit Aufgaben beinhaltet. Diese Listen sind entweder intern oder dienen als Outbox bzw. Inbox eines anderes Teams. Die Aufgaben möchte Samu später aus dem Ticketsystem in das neue Tool synchronisieren. Am Ende stellt er sich auch eine einfache Ansicht der Aufgaben für die Teams vor, ohne dass diese mit Informationen konfrontiert werden, die üblicherweise nur für das Projektmanagement relevant sind.

entites overview
Abbildung 5. Datenmodell

Samu wirft noch einen kurzen Blick auf die Dokumentation [17] und legt seine erste Entität an (Listing 3).

Listing 3. Erzeugen einer Entität
jhipster entity team

Samu beantwortet alle Fragen gewissenhaft. Allerdings findet er es überhaupt nicht "hip" für jede Entität alle Fragen erneut beantworten zu müssen (Abbildung 6). Da auch schon Feierabend ist, setzt er seine Änderungen zurück und beschließt morgen Jennifer zu fragen, ob die CLI die einzige Möglichkeit ist Entitäten anzulegen.

Entity Generator
Abbildung 6. Konfiguration einer Entität mit dem Entity Generator

JDL

Jennifer schmunzelt am nächsten Morgen ein wenig und fragt, wieso er denn nicht das JDL Studio [18] verwende, um dort sein Entitätenmodell mit einer domänenspezifischen Sprache zu beschreiben. Als Bonus würde das Model direkt im Browser als Klassendiagramm gerendert. Neben Entitäten könne man auch ganze Anwendungen und sogar Microservices mit JDL definieren und somit fast vollständig ohne CLI arbeiten (Listing 4).

Listing 4. Anwendungskonfiguration via JDL
application {
  config {
    applicationType monolith,
    baseName javaprosample
    packageName com.gitlab.atomfrede.javapro.sample,
    authenticationType jwt,
    prodDatabaseType postgresql,
    buildTool gradle,
    clientFramework vuejs,
    enableTranslation true,
    nativeLanguage de,
    languages [en, de]
  }
}

Samu liest noch ein wenig Dokumentation und schreibt sein JDL Model. Er exportiert es und importiert es in seine Anwendung mit jhipster import-jdl jhipster-jdl.jh. Nach dem Import inspiziert Samu was JHipster alles generiert hat. Neben Liquibase Changesets, um die Datenbanktabellen anzulegen, wurden alle nötigen Spring Data Repositories und Entitätsklassen mit entsprechenden JPA/Hibernate Annotationen erzeugt. Für jede Entität steht eine einfache CRUD Oberfläche (Abbildung 8 & Abbildung 9) zur Verfügung, die über eine REST API mit dem Backend kommuniziert. Alle diese Schritte hätten Samu sehr viel Zeit gekostet und für einen Prototypen hätte er vermutlich keine Datenbankmigrationen eingerichtet, sodass er dies später hätte nachholen müssen. Samu pushed seinen aktuellen Stand ins Versionskontrollsystem und macht erstmal Mittagspause, um sich später mit dem Betrieb in der Cloud und automatischen Builds zu beschäftigen.

Listing 5. Entitätsdefinition via JDL
entity Team { (1)
        name String required minlength(3) (2)
}

entity Workspace {
        name String required
}

entity IssueList {
        name String required
}

entity Issue {
        title String required
}

entity ListConnection {
        name String required
        type ListRole required
}

 enum ListRole {
    INBOX, OUTBOX, INTERNAL
}

relationship OneToOne { (3)
        Team{workspace(name)} to Workspace{team(name)} (4)
}

relationship OneToMany {
        ListConnection to IssueList{listConnection(name)}
  ListConnection to Workspace{listConnection(name)}
}

relationship OneToMany {
        IssueList to Issue{issueList(name)}
}

paginate Team, Workspace, IssueList with pagination (5)
paginate Issue with infinite-scroll
1 Eine Entität benötigt einen Namen.
2 Pro Feld können verschiedene Constraints angegeben werden, die sowohl im Backend als auch im Frontend durchgesetzt werden.
3 Neben OneToOne und OneToMany gibt es noch ManyToMany Beziehungen.
4 Im Standard werden in der generierten UI die IDs der referenzierten Entität angezeigt. Dies kann überschrieben werden, sodass z.B. die Namen angezeigt werden.
5 Weitere Optionen wie die Verwendung von DTOs oder welche Art von Pagination verwendet werden soll.
JHipster JDL
Abbildung 7. Samus JDL Model
CRUD UI
Abbildung 8. Generierte CRUD Oberfläche (Übersichtstabelle) mit automatisch erzeugten Demodaten
CRUD UI Detail
Abbildung 9. Generierte CRUD Oberfläche (Detailansicht)

Betrieb und CI/CD

Samu möchte sich mit dem Betrieb von Servern und Datenbanken nicht beschäftigen. Daher verwendet er Plattformen wie Heroku oder Cloud Foundry, welche die Konfiguration von Zertifikaten, Datenbanken und Webservern erledigen. JHipster unterstützt neben Heroku und Cloud Foundry auch AWS, Kubernetes und Azure [19]. Samu entscheidet sich für Heroku, da er dort bereits ein Konto hat und sein lokales System alle Voraussetzungen erfüllt, um mit Heroku zu kommunizieren [20].

Mit jhipster heroku startet Samu den Heroku Subgenerator. Er muss einen Namen für die Anwendung vergeben und auswählen in welcher Region (us oder eu) die Anwendung betrieben werden soll. Er entscheidet sich für Europa. Um nicht immer die Anwendung auf seinem Rechner bauen zu müssen und die .jar Datei hochzuladen, entscheidet er sich dafür die Anwendung von Heroku bauen zu lassen (Abbildung 10). Der Subgenerator erstellt eine Anwendung auf Heroku, erweitert die Buildfiles (pom.xml oder build.gradle) um die nötigen Plugins, erstellt automatisch die passende Datenbank auf Heroku, konfiguriert die Datenbankverbindung der Anwendung und deployed den aktuellen Stand. Nach ein paar Minuten ist die Anwendungen unter https://javaprosample.herokuapp.com/ erreichbar.

Heroku Subgenerator
Abbildung 10. Erzeugen einer Heroku Konfiguration

Samu ist ein großer Automatisierungsfan. Natürlich möchte er daher auch seinen Prototypen automatisch bauen, testen und im besten Fall auch direkt in der Cloud deployen lassen. Da er Gitlab nutzt, verwendet er den JHipster CI/CD Subgenerator [21], um automatisch eine passende Konfiguration für sein Projekt zu erzeugen. Neben Gitlab werden auch Jenkins Pipelines, Travis und Azure Pipelines unterstützt. Nach dem Starten des CI/CD Generators mit jhipster ci-cd muss er noch einige Details auswählen. Samu entscheidet sich seine Anwendung mit Sonar zu analysieren und von Gitlab Aktualisierungen direkt auf Heroku bereitzustellen. Insgesamt erzeugt JHipster eine Pipeline, die aus mehreren Schritten besteht. Der Deployschritt ist dabei im Standard optional. Er committed und pushed seine Konfiguration und GitLab startet direkt die Pipeline mit der erzeugten Konfiguration (Abbildung 11). Seit Version 6 werden Unit- und Integrationstests in verschiedenen Stufen der Pipeline ausgeführt und statt Junit 4 wird bereits Junit 5/Jupiter zur Ausführung aller Tests verwendet.

Gitlab Pipeline
Abbildung 11. Erfolgreicher Ablauf der erzeugten Gitlab CI Pipeline

Fazit und Ausblick

Samu konnte dank JHipster eine moderne Webanwendung innerhalb eines Tages erzeugen und in der Cloud betreiben. Er konnte sich auf sein eigentliches Ziel konzentrieren und muss sich nun noch um die Synchronisation der Aufgaben aus dem bestehenden Ticketsystem kümmern und eine eigene UI für die Benutzer der Anwendung bauen. Alle Schnittstellen und Datenbankentitäten wurden von JHipster erzeugt und können verwendet werden. Dank der JDL kann Samu sowohl die Konfiguration der Anwendung (z.B. OAuth2 statt JWT) als auch sein Datenmodell leicht anpassen.

Nachdem JHipster 6 den Sprung auf Spring Boot 2.1 und Java 11 vollzogen hat konzentriert sich das Team auf den Feinschliff vorhandener Funktionen. Neben der Weiterentwicklung der JDL, sodass alle Optionen via JDL definiert werden können, ist geplant Aktualiserungen von bestehenden Anwendungen zu vereinfachen, insbesondere, wenn viel eigener Code geschrieben wurde. Dadurch soll JHipster noch mehr zu einem Framework werden, das nicht nur das "Scaffolding" einer Anwendung vereinfacht wird sondern den kompletten Lebenszyklus und Entwicklungsprozess abdeckt.

Durch die Blueprint Funktionalität, die seit Version 6 den Beta Status verlassen hat, wurde das Interesse von weiteren Communities geweckt. Das JHipster Core Team portiert die Scaffolding Funktionen momentan zu .net/C#. Maintainer von Micronaut und Quarkus versuchen ebenfalls einen JHipster Blueprint für beide Frameworks zu bauen.

Über den Autor

author Frederik Hahne arbeitet als Software Entwickler bei der WPS Management GmbH in Paderborn an der offenen B2B Integrationsplattform wescale (wescale.com). Er ist Organisator der Java User Group Paderborn und hat in Paderborn die lokale Devoxx4Kids Gruppe ins Leben gerufen. Seit 2015 ist er Mitglied des Java Hipster Core Teams und betreut momentan hauptsächlich den Gradle Build und den vue.js Blueprint des Projekts. Er twittert unter @atomfrede und bloggt gelegentlich unter https://atomfrede.gitlab.io/.

Quellen