JEE Klassen > TypeScript

Ein JEE Backend ist mit wenig Aufwand über eine JSON basierte REST-Schnittstelle zur Verfügung gestellt. An diesem Punkt kann sich der Java Entwickler zurücklehnen und die Angular Entwickler machen lassen. Als Fullstackentwickler ( oder freundlicher Java Entwickler ) können wir uns ( oder unseren Kollegen ) das Leben leicht leichter machen.

In aller Regel werden wir unsere Angular Anwendung mittels TypeScript schreiben und hier immer wo es geht auf ein typsicheres Vorgehen setzen. Das erfordert es allerdings eigene TypeScript Definitionsdateien zu schreiben welche unsere Datenmodelle ( die wir ja bereits in Java geschrieben haben ) in die TypeScript-Welt bringt. Ein Maven-Plugin welches hier Hilft:

cz.habarta.typescript-generator # typescript-generator-maven-plugin

Dieses registrieren wir in unserer pom.xml, versehen mit einigen zusätzlichen Konfigurationen und wir erhalten eine *.d.ts Datei welche wir in die Angular Entwicklung mit einfließen lassen können:

    <build>
        <plugins>
            <plugin>
                <groupId>cz.habarta.typescript-generator</groupId>
                <artifactId>typescript-generator-maven-plugin</artifactId>
                <version>2.12.476</version>
                <executions>
                    <execution>
                        <id>generate</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <phase>process-classes</phase>
                    </execution>
                </executions>
                <configuration>
                    <jsonLibrary>jackson2</jsonLibrary>
                
                    <classesFromAutomaticJaxrsApplication>true</classesFromAutomaticJaxrsApplication>
                    <outputKind>module</outputKind>
                </configuration>
            </plugin>
        </plugins>
    </build>

Das funktioniert relativ gut, scheitert natürlich wenn wir mittels JSON-Mapper in die Struktur der generierten JSON-Nachricht eingreifen. Zum Beispiel übermitteln wir bei Relationen (User hat eine Liste von Projekten) sehr häufig lediglich die IDs der Projekte. In unseren TypeScript – Modelklassen werden wir aber gemäß Java-Klassen ein Objekt vom Typ „Projekt“ vorfinden. Hier müssen wir entweder manuell eingreifen, die generierten Klassen erweitern / überschreiben. Oder im Programm durch entsprechende Casts gegensteuern.

Werbeanzeigen

WildFly 16 released!

Seit gestern steht die Version 16.0.0.Final des WildFly zum Download bereit (http://wildfly.org/downloads/).

Bei einem kurzen Blick darauf sind mir zwei interessante Neuerungen aufgefallen:

  • Der Server unterstützt nun auch Microprofile Metrics.
  • Mit Hilfe von Galleon (https://github.com/wildfly/galleon), einem neuen Provisionierungstool, können Serverinstallationen erzeugt werden, die nur die jeweils benötigten Module enthalten.

Beide Punkte zeigen, dass der Server sich immer weiter in Richtung Cloud Readiness entwickelt.

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Angular – eigene ReactiveForm Komponenten

In größeren Projekten werden wir relativ schnell den Punkt erreichen in dem sich der ein oder andere Entwickler denkt: „Moment das hatten wir doch schon mal“. Wenn noch nicht geschehen ist das der Zeitpunkt um für den so identifizierten Bereich / Funktion eine eigene Komponente aus zu bilden. Wenn es sich dabei um ein Form-Element handelt mag es zuerst einmal nicht so offensichtlich sein wie man das am besten bewerkstelligt…

Nehmen wir ein einfaches konkretes Beispiel: in unserer Anwendung haben wir eine Auswahlbox von Benutzern durch die z.B. eine Aufgabe, ein Projekt etc. zugeordnet werden soll. Diese Auswahl von Benutzern wird nun an verschiedenen Stellen der Anwendung benötigt. Also entwickeln wir eine eigenen Komponente, was dank Angular-CLI ja auch ein Kinderspiel ist:

ng generate component UserSelector

So einfach ist es in diesem Beispiel jedoch erst einmal nicht. Das Ziel soll es ja sein unsere Komponente als Teil eines Formulars zu verwenden und eine Deklaration analog zu den Standard-HTML Elementen zu erreichen:

<form [formGroup]="form">
  <label for="user">Benutzer: </label>
  <app-user-selector formControlName="userId" id="user"></app-user-selector>
  <label for="comment">Kommentar: </label>
  <input type="text" id="comment" formControlName="comment"/>
</form>

app.component.html > Verwendung unserer Komponente

wir wollen hier einen ReactiveForm-Ansatz verfolgen bei dem die Registrierung von Datenmodel und Validatoren in der Komponentenklasse geschieht, dazu wird hier der Name des Controls mittels ‚formControlName‘ eingesetzt. Die Alternative wäre der Einsatz von „ngModel“

In unserem Projekt setzen wir PrimeNG als Komponentenbibliothek ein, unsere UserSelector Komponente wird also lediglich eine Art Wrapper, hier das Template:

<span [formGroup]="form">
  <p-dropdown
    [options]="users"
    optionLabel="name"
    class="form-control"
    [autoDisplayFirst]="false"
    dataKey="id"
    formControlName="value"
  ></p-dropdown>
</span>

user-selector-component.html > Template unserer Komponente

wir verwenden hier eine PrimeNG Komponente ‚p-dropdown‘. Für unsere eigene Komponente spannend ist hier die Erzeugung einer eigenen FormGroup innerhalb unserer Komponente, das ermöglicht uns die PrimeNG Komponente innerhalb „ganz normal“ mit unserer Komponenten-Klasse zu verbinden

import { Component, OnInit, forwardRef } from '@angular/core';
import { FormGroup, ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder } from '@angular/forms';
import { DemoService } from '../demo.service';

// 1
@Component({
  selector: 'app-user-selector',
  templateUrl: './user-selector.component.html',
  styleUrls: ['./user-selector.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => UserSelectorComponent),
      multi: true
    }
  ]
})
//2
export class UserSelectorComponent implements ControlValueAccessor {
  users: any[];

  form: FormGroup;

  //3
  constructor(private service: DemoService, builder: FormBuilder) {
    service.getAll().subscribe(r => (this.users = r));

    this.form = builder.group({
      value: ['']
    });
    //4
    this.form.controls.value.valueChanges.subscribe(c => {
      this.onChange(c.id);
    });
  }

  //5
  writeValue(obj: number): void {
    if (obj) {
      const value = this.users.find(e => e.id == obj);
      if (value) this.form.patchValue({ value });
    }
  }

  //6
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  onChange: any = () => {};
  onTouched: any = () => {};
}

user-selector-component.ts > Komponentenklasse

1. die Metadaten für unsere Komponente. Neu an dieser Stelle ist die Registrierung unserer Komponente als Value-Accessor über das Provider Array, sehr einfach ausgedrückt ist dieses Deklaration bei jeder dieser Komponenten technisch bedingt genau so zu deklarieren ( sehr spannende Angular-Hintergrund-Themen zu dieser Deklaration: ‚Multi Providers‘ und ‚forwardRef‘ )

2. wir Implementieren das Interface ControlValueAccessor

3. Konstruktor. Neben einem Demo-Service ( der uns die Daten für unsere Liste ermittelt), lassen wir uns auch den FormBuilder von Anuglar injizieren um die FormGroup auf zu bauen

4. hier wird es spannender: wir registrieren einen Change-Listener an unserer internen FormControl ( die PrimeNG Dropdown-Liste ). Wenn der User hier eine Änderungen vornimmt wollen wir dieses Event „weiter geben“ und den Verwender unserer Komponente informieren, dazu wird die Methode onChange ( Interface ) verwendet. ( In unserem Beispiel nehmen wir hier auch eine Umwandlung vor: unserer DropDown-Liste liefert ein komplexes User-Objekt, in unserer Anwendung, also in den anderen fachlichen Objekten verwenden wir lediglich die Id.

5. writeValue ( Interface ) ist die Methode welche aufgerufen wir um ein Objekt welches aus dem Model kommt in der UI darzustellen, also beim Rendern oder bei Änderungen der Form. Wir bekommen an dieser Stelle die Id des bereits selektierten Benutzers, anhand dessen wir das komplexe User-Objekt aus unserem Array ermitteln und unser internes Formular damit aktualisieren.

6. Interface Methoden um die Registrierungen von EventListenern zu ermöglichen

Das war’s damit haben wir eine Komponente welche ganz einfach in unseren Formularen eingesetzt werden kann. Auf diese Weise lassen sich natürlich auch gänzlich eigene Komponenten ( ohne PrimeNG ) implementieren oder wesentlich komplexere wiederverwendbare Komponenten.

Noch mal gucken? > GITHUB

Angular strukturiert 2/2 – @NgModule

Umfangreiche Angular Projekte bestehen in aller Regel aus sehr vielen Komponenten und Service-Klassen die in eine sinnvolle Struktur gebracht werden müssen. Darüber hinaus macht es auch aus Performance Gründen Sinn die eigene Anwendung zu unterteilen. Im ersten Teil haben wir einen Blick auf die Organisation mittels Workspace geworfen. Heute schauen wir uns die Verwendung von Modulen an.

Jede Angular Anwendung besteht aus einem NgModule. Bei genauerer Betrachtung eher aus sehr vielen Modulen, ist doch auch die Core-Bibliothek von Angular in Modulen aufgeteilt ( BrowserModule. FormsModule … ) die wir bei Bedarf importieren. Aber auch unsere eigene Anwendung können wir mittels Module in einzelne Blöcke unterteilen. Neben der besseren Struktur unserer Anwendung hat das noch einen ganz entscheidenden Vorteil: Performance. Wir können komplexe Angular Anwendung in fachliche Teilbereiche gliedern und für jeden dieser Teile ein eigenes Modul implementieren. Ein User der unsere Anwendung aufruft wird nun nicht beim ersten Zugriff unsere gesamte Anwendung laden müssen sondern nur den Bereich ( Modul ) den er gerade anfragt. Erst wenn der User über entsprechende Navigation auf andere Bereiche zugreift werden diese Anwendungsteile geladen. Hier in Kürze wie das geht:

1. ng new [app-name]

Neue Anwendung erzeugen, die in unserem Beispiel nur die Startseite beinhaltet +  gemeinesame Komponente + gemeinsame Services

2. ng generate module [module-1]

Neues Modul generieren, z.B. „materials“ oder „customers“ oder „users“

3. Routing anpassen

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  { path: 'customer', 
   loadChildren: './customers/customers.module#CustomersModule' },
  { path: 'material', 
   loadChildren: './materials/materials.module#MaterialsModule' }
];

Der spannende Teil ist hier die Deklaration der Navigation auf die neuen Module, über die Eigenschaft „loadChildren“ wird hier der relative Pfad zur Module Datei angegeben + der Klassenname des Moduls (getrennt mittels „#“). Der Build Prozess resultiert dann in separaten JavaScript Dateien für die jedes Modul welche nur beim auslösen der entsprechenden Navigation abgerufen wird.

Zusammen mit Teil 1 hier auf github

Angular strukturiert 1/2 – workspace

Umfangreiche Angular Projekte bestehen in aller Regel aus sehr vielen Komponenten und Service-Klassen die in eine sinnvolle Struktur gebracht werden müssen. Darüber hinaus macht es auch aus Performance Gründen Sinn die eigene Anwendung zu unterteilen. Im ersten Teil werfen wir einen Blick auf die Organisation mittels Workspace.

In allen unseren Projekten werden wir den ein oder anderen Service Implementieren oder Komponenten ausprägen die nicht nur für das vorliegende Projekt interessant sind, sondern auch für anderen unternehmenseigene Projekte. Eine solche eigene Basis-Bibliothek lässt sich natürlich als eigenständiges NPM-Packet entwickeln und über ein internes Node-Repository ausliefern. Insbesondere zur frühen Entwicklungszeit, wenn sich noch viel in den Bibliotheken tut verkompliziert dieses Vorgehen aber die Bereitstellung und Verwendung. Eine Alternative (die sich aber später ebenfalls problemlos als NPM-Paket veröffentlichen lässt) ist seit der Version 6 in Angular-CLI vorhanden: „Libraries“ .

Die Grundidee ist einfach: ein einzelnes Angular-Projekt  (workspace)  dient als Basis für die Entwicklung unserer Anwendungen und Bibliotheken. Für jede Bibliothek ( library) oder Anwendung( application ) werden über entsprechende CLI-Befehle eigenständige Bestandteile generiert. Hier das grundsätzliche Vorgehen mittels Angular-CLI Befehle:

1.  ng new [workspacename]

Optional kann hier die Option: –create-application=false gewählt werden um die Demo-Anwendung nicht initialisieren zu lassen

2. ng generate library [name-1] –prefix=[prefix]

Eine erste Bibliothek, die wir später in unseren Projekten verwenden können. Hier entwickeln wir die gemeinsamen Komponenten und Services.

3. ng build [name-1]

Bibliothek bauen, optional kann hier auch das Flag –watch aktiviert werden um bei Änderungen den Build-Prozess zu triggern

4. ng generate application [name-2]

Eine Anwendung generieren. Hier arbeiten wir wie gewohnt, können aber auf die Services/Komponenten unserer Library zugreifen (nicht vergessen das Utils-Module zu importieren)

5. ng serve [name-2]

Entwicklungs-Server starten für definierte Anwendung…

Sowohl die Bibliotheken, als auch die einzelnen Anwendungen können später ganz autonom verteilt oder deployt werden. Im zweiten Teil werfen wir einen Blick auf die strukturierung mittels NgModule.

Bis dahin:

https://github.com/GEDOPLAN/anuglar-modulesD

Transaktionssteuerung mit @Transactional

Transaktionen sind nötig, um Daten atomar, konsistent, isoliert und dauerhaft bearbeiten und speichern zu können (ACID-Prinzip). Methoden von CDI-Beans können durch eine simple Annotation mit einem aktiven Transaktionskontext versehen werden.

Transaktionsinterceptor mit dem Binding @Transactional

Schon seit der Version 1.1 von CDI steht ein global aktivierter Transaktionsinterceptor zur Verfügung. Er wird mit der Annotation @Transactional einer Methode einer CDI-Bean zugeordnet:

public class SomeService {
  ...
  @Transactional
  public void doSomethingTransactionally() {
    ...
  }

Beim Aufruf der Methode ohne aktive Transaktion wird nun eine Transaktion gestartet und nach dem Verlassen der Methode wieder geschlossen. Die Regeln für das Transaktionsende sind etwas seltsam:

  • Endet die Methode mit einem Return, wird ein Commit versucht.
  • Wirft die Methode eine sog. System Exception, resultiert dies in einem Rollback. System Exceptions sind i. W. die unchecked Exceptions (ergänzt um die RemoteException, die aber explizit kaum eine Rolle spielt).
  • Wirft die Methode eine Application Exception – das sind alle anderen Exeptions – wird ein Commit (!) versucht.

Insbesondere der letzte Punkt ist (zumindest mir) unverständlich: Für mich modellieren Exceptions immer Ausnahmesituationen, die den jeweiligen Geschäftsprozess ungültig machen. Glücklicherweise kann man mit einem Parameter der Annotation auch in diesem Fall ein Rollback ansteuern:

  @Transactional(rollbackOn=Exception.class)
  public void doSomethingTransactionally() {

Transaktionsmodus

Die Art der Transaktionssteuerung durch @Transactional kann mit dem Parameter value bestimmt werden:

  • TxType.REQUIRED ist der Default, der in den allermeisten Fällen passend ist. Beim Aufruf einer davon betroffenen Methode wird geprüft, ob bereits eine aktive Transaktion vorliegt. Wenn ja, wird diese für den Methodenaufruf genutzt. Andernfalls wird eine neue Transaktion gestartet und am Methodenende wieder beendet.
  • TxType.REQUIRES_NEW startet bei Methodenbeginn stets eine neue Transaktion, die am Methodenende wieder beendet wird. Eine ggf. bereits aktive Transaktion wird zuvor suspendiert. Dieser Modus sollte dann verwendet werden, wenn die Ergebnisse des Methodenaufrufs auch dann dauerhaft abgespeichert werden sollen, wenn die umgebende Transaktoin zurückgerollt wird. Beispiel: Auditing/Protokollierung von Methodenaufrufen.
  • TxType.MANDATORY verlangt eine aktive Transaktion bei Methodenbeginn und wirft andernfalls eine TransactionRequiredException. Dieser Modus ist könnte prinzipiell für Repositories (a.k.a DAOs) interessant sein, da deren feingranulare Methoden i. A. von einem Service aufgerufen werden, der selbst transaktional ist. Leider werden Lifecycle-Methoden (@PostConstruct-annotierte Methoden etc.) nicht durch die @Transactional-Interceptoren intercepted. Häufig werden darin Repository-Methoden aufgerufen. Dann sollte für Repositories wie auch bei anderen Services TxType.REQUIRED verwendet werden.
  • Die weiteren Modi TxType.NOT_SUPPORTED, TxType.SUPPORTS und TxType.NEVER sind i. A. unbrauchbar.

Danke für’s Lesen und bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Alles gesund? Health checking mit MicroProfile Health

Im Bereich von Microservices und verteilten Systemen – aber nicht nur dort – ist es wichtig zu wissen, ob ein Service/System ordnungsgemäß läuft oder gestört ist. Im MicroProfile (https://microprofile.io) ist mit MicroProfile Health ein Teil enthalten, der es in Kombination mit CDI sehr einfach macht, Health Checking in EE-Anwendungen zu integrieren.

Health Check Callbacks

Die Idee von MicroProfile Health ist, dass Anwendungen auf EE-Servern oder auf Basis von MicroProfile-Frameworks ein oder mehrere Methoden zum Prüfen des Gesundheitszustandes enthalten. Der Server sammelt die Zustände bei Bedarf ein und aggregiert daraus einen Gesamtzustand, der auf dem Rest-Endpoint /health veröffentlicht wird:

GET /health

200 OK
{
  "outcome": "UP",
  "checks": [{
    "name": "Service1",
    "state": "UP",
    "data": {
      "memory": 180672240
    }
  }, {
    "name": "Service2",
    "state": "UP"
  }]
}

Die Callback-Methoden heißen – etwas uninspiriert – call und stammen aus dem Interface org.eclipse.microprofile.health.HealthCheck. Der Server sucht sich automatisch alle Klassen zusammen, die dieses Interface implementieren und mit dem CDI-Qualifier org.eclipse.microprofile.health.Health versehen sind:

@ApplicationScoped
@Health
public class HealthCheck1 implements HealthCheck {

  @Override
  public HealthCheckResponse call() {
    ...

Das Prüfergebnis können die Methoden bequem mittels Builder-Pattern zusammensetzen:

  public HealthCheckResponse call() {
    return HealthCheckResponse
        .named("Service1")
        .up()
        .withData("memory", Runtime.getRuntime().freeMemory())
        .build();

Neben up gibt es auch down sowie die Methode status, der der Gesundheitszustand als Parameter übergeben wird.

Gesamtgesundheit

Bei einem GET-Request auf den Pfad /health werden alle verfügbaren call-Methoden aus allen Anwendungen des Servers aufgerufen und zu einem Gesamtzustand kombiniert. Liefern alle Checks UP, ist das Attribut outcome im Gesamtergebnis ebenfalls UP und der Response hat den Status 200 OK. Andernfalls wird der Response mit 503 Service unavailable ausgeliefert und outcome ist darin DOWN.

Plattformen

MicroProfile Health wird u. a. von folgenden JEE-Servern unterstützt:

  • OpenLiberty (mit aktiviertem Feature mpHealth-1.0).
  • Payara.
  • WildFly (Achtung: WildFly hat unterschiedliche Ports für Applikations- und Management-Zugriffe, im Default 8080 und 9990. Der o. a. REST-Endpoint /health steht über den Management-Port zur Verfügung).

Die genannten Klassen befinden sich bspw. in der Maven-Dependency org.eclipse.microprofile:microprofile.

Demo

In https://github.com/GEDOPLAN/health-demo finden Sie ein Beispielprojekt mit zwei Health Checks, deren Ergebnis über eine Webseite beeinflusst werden kann. Die Anwendung kann als WAR-File auf einen der genannten Server deployt werden. Alternativ können über vorkonfigurierte Maven-Profile Docker-Images zum direkten Ausprobieren erstellt werden.

Ausblick

Im Beispiel wird als Zusatzinformation die Menge freien Speichers hinzugefügt, was im Kontext von Health Checking nicht grundsätzlich falsch ist, aber doch eher eine Metrik darstellt. Für deren Veröffentlichung hält MicroProfile eine weitere Teilspezifikation bereit: MicroProfile Metrics. Sie wird Gegenstand eines der nächsten Blog Posts sein – stay tuned!

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/