It's the client, stupid!

AngularBeans: Ein neuer Ansatz für AngularJS und Java EE

Bessem Hmidi, Stephan Rauh

©Shutterstock/Africa Studio

Bei AngularBeans handelt es um einen neuen Ansatz zur Integration von AngularJS mit einem Java-EE-Backend, der in letzter Zeit für Furore in der Java-EE-Welt gesorgt hat.

Im Java-Ökosystem mangelt es nicht an Web Frameworks, für jeden Geschmack steht eines zu Verfügung. Ganz egal ob man aktionsbasierte, komponenten- oder anfragegetriebene favorisiert – es gibt für jeden das passende Framework. Wenn man jedoch einen näheren Blick auf die Entwicklungen im Web Development wirft, kommt man um folgende Einschätzung nicht herum: Wir befinden uns in der Ära der Single- Page-Applikationen, des Client-seitigen Renderings und des Echtzeitwebs.

Die User sind viel anspruchsvoller geworden, sie geben sich mit einem schwarz/weißen Bildschirmterminal nicht mehr zufrieden. Vielmehr sind sie an hochgradig responsive, interaktive User Interfaces gewöhnt, die weitaus unterhaltsamer und effizienter zu bedienen sind. Diese Bedürfnisse haben zahllose moderne Client-seitige JavaScript-Frameworks wie Ember.js, react.js, meteor.js und natürlich AngularJS hervorgebracht. Statt als klassischen Server zur Generierung von dynamischen Sichtfeldern betrachten sie alle den Server als „reinen“ Service Provider, der ausschließlich der Prozessierung und Bereitstellung von Daten dient. Die meisten – vielleicht sogar alle – Entwickler betrachten serverseitiges Rendering als Ding der Vergangenheit. Technisch gesehen war serverseitiges Rendering in früheren Zeiten unumgänglich, aber heutzutage befindet sich die Browser-Technologie auf einem Niveau, das es uns erlaubt, sowohl Rendering als auch die Anwendungslogik auf die Client-Seite zu verlagern.

It’s the client!

In anderen Worten: wenn wir über MVC oder MV[1] reden, lässt sich nur schwerlich bestreiten, dass die Architektur auf der Client-Seite aufgesetzt werden sollte, besonders vor dem Hintergrund der Leistungsfähigkeit aktueller Client-Engines. Die JavaScript-Engines sind mittlerweile um einiges schneller als noch vor wenigen Jahren, und die Client-seitige Webentwicklungstoolchain ist erwachsen geworden. Letztere unterstützt heute Aufgaben wie Abhängigkeitsmanagement, Testing, Debugging und Build-Automation. Man denke nur an Bower, Grunt, Yeoman, etc.

Ein modernes serverseitiges Framework sollte sich also bewusst darüber sein, dass auf der anderen Seite des Netzwerkadapters ein leistungsfähiger und intelligenter Client sitzt. Auf lange Sicht wird das klassische Paradigma – ein Thin-Client wird von einem statusorientierten Server bedient – von einem anderen Architekturmuster ersetzt werden, bestehend aus einem statusbehaftetem („stateful“) Client und einem mehr oder weniger statuslosen Server.

Ein Client Framework auswählen, das mit einem Java-EE-Server harmoniert

Gegenwärtig ist AngularJS auf dem Markt der Frameworks für JavaScript-Single Page-Applikationen klar führend. Es ist sehr gut auf unsere heutigen Bedürfnisse ausgerichtet. Aber was ist mit dem serverseitigen Part? Naturgemäß sind damals die meisten der aktuellen Java-Webframework-Projekte begonnen worden, als JavaScript lediglich für Animationen oder Formvalidierung genutzt wurde. Erinnert sich noch jemand an die Idee des progressiven Enhancements? Heute ist sie in Vergessenheit geraten, aber als die meisten der jetzigen Java-Webframeworks entstanden, war genau dieses Paradigma ziemlich angesagt.

Wenn jedoch ein JS-Framework wie AngularJS involiert ist, eignen sich diese Java-Webframeworks wenig. Als eine etwas sauberere Alternative würde sich eine AngularJS/JaxRS-Lösung anbieten. Oder eine NG + SpringMVC- bzw. NG + Ozark – JSR 371(aka MVC 1.0)-Herangehensweise. Möglich, aber diese Lösungen sind zu HTTP-zentriert. Dadurch verliert man die nützliche Abstraktionsschicht, die etwa Frameworks wie JSF oder GWT/Vaadin anbieten. Was aber, wenn wir sowohl das Abstraktionslevel von Java EE als auch die Kraft von AngularJS nutzen könnten? Dahinter steht die Idee, eine nahtlose Verbindung eines ausgewachsenen Java-EE-Backends mit einem AngularJS-Klienten herzustellen, der die Seite rendert und auf die User-Interaktionen achtet. Man stelle sich vor: kein XHTML mehr, kein JSP mehr und kein komplexer Lifecycle mehr. Stattdessen nur noch reines HTML5 und JavaScript auf der Client-Seite, aber – ähnlich wie bei JSF – ohne Boilerplate-Code für die Integration. Wäre das nicht was?

AngularBeans

Hier kommt AngularBeans ins Spiel. Dieses Framework enttarnt Java EE 7 CDI Beans als versteckte AngularJS-Services. Die Idee ist einfach: Man schreibt seine JavaBeans, injiziert sie auf der AngularJS-Seite, und was dazwischen passiert, ist Angelegenheit von AngularBeans.

AngularBeansArchitecture

Erste Schritte mit AngularBeans

Alles, was Sie brauchen, um mit AngularBeans loszulegen, ist ein Java-EE-7-Webprofil oder einen vollständigen Application Server und Ihre bevorzugte IDE. Es gibt sogar einen Maven-Archetypen auf Maven Central, der Ihnen den Einstieg leicht macht.

Sofern Sie den Maven-Central-Archetypenkatalog noch nicht installiert haben, müssen Sie die Katalog-URL http://repo.maven.apache.org/maven/archetype-catalog.xml hinzufügen. Dabei handelt es sich um die benötigten Details für den Start eines neuen Projekts ausgehend vom „helloWorld“-Archetypen.

groupId: com.github.bessemHmidi
artifactId: angularBeans-helloWorld-archetype
version: 1.3-RELEASE

Das daraus resultierende Projekt enthält eine pom.xml, die zur SNAPSHOT-Version von AngularBeans führt. Wahrscheinlich werden Sie die Stable-Version bevorzugen, darum sollten Sie vor dem ersten Test die pom.xml wie folgt ändern:

<dependency>
    <groupId>com.github.bessemHmidi</groupId>
    <artifactId>angularBeans</artifactId>
    <version>1.0.2-RELEASE</version>
</dependency>

Die Projektstruktur

AngularBeansFolders

In dieser Projektstruktur enthält der Webapp-Ordner zwei JavaScript-Abhängigkeiten: die AngularJS lib (V 1.4.5) sowie die SockJS lib (0.3.4). Auf der CSS-Seite wird Bootstrap 3.1.1 benutzt.

Eigentlich sind Bootstrap und SockJS optional. Das einzige obligatorische Tool ist AngularJS. SockJS kann aber nützlich sein, wenn Sie ältere Browser unterstützen wollen. Diese Library wird von AngularBeans erkannt.

Sofern sie vorhanden ist, wird sie bei allen Real-Time-Behaviors des Frameworks anstelle der Standardnutzung des nativen Web Sockets gebraucht. Auf diese Weise kann man Web Sockets mit älteren Browsern nutzen, die Web Sockets eigentlich nicht unterstützen.

Nebenbei bemerkt, stellt das Ordnerlayout lediglich eine Standardstruktur dar. Mit AngularBeans können Sie jede Ordnerstruktur benutzen, die Ihnen gefällt. Zu beachten ist, dass angular-beans.js eine leere Datei ist. AngularBeans liest diese Datei nicht wirklich aus. Sie ist nur ein Platzhalter.

Stattdessen wird der tatsächliche Inhalt von einem Servlet bereitgestellt. Die Datei ist nur optional und wird lediglich dazu verwendet, einige IDE-Validatoren davon abzuhalten, sich über die fehlende „angular-beans.js“-Datei zu beschweren.

Lassen Sie uns jetzt einen Blick auf die „HelloAngularBeans“-Klasse auf der Serverseite werfen:

import angularBeans.api.AngularBean;
import angularBeans.api.NGReturn;
import angularBeans.api.http.Get;

@AngularBean
public class HelloAngularBeans {
	@Get
	@NGReturn(model = "message")
	public String sayHello(String name) {
		return "hello " + name + " from AngularBeans !";

	}
}

Und hier der Controller auf der Serverseite:

function MyCtrl(helloAngularBeans){
 var vm=this;

 vm.helloAngularBeans=helloAngularBeans;
}

Und zum Schluss das HTML:

<!DOCTYPE html>
<html data-ng-app="myApp">
<head>

<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="css/app.css" />

<script type="text/javascript"
	src="bower_components/angularjs/angular.min.js"></script>
<script type="text/javascript"
	src="bower_components/sockjs/sockjs.min.js"></script>

<script type="text/javascript" src="angular-beans.js"></script>

<script type="text/javascript" src="js/filters.js"></script>
<script type="text/javascript" src="js/services.js"></script>
<script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/directives.js"></script>
<script type="text/javascript" src="js/module.js"></script>

<meta charset="ISO-8859-1">
<title>hello world</title>
</head>

<body ng-app="myApp">
…
    <div ng-controller="MyCtrl as vm">
       <div class="form-group">
           <fieldset>
		<label>your name: </label> <input ng-model="name" class="form-control">
	    </fieldset>
	</div>

	<button ng-click="vm.helloAngularBeans.sayHello(name)" class="btn btn-primary">click me</button>
	<span color="blue">message {{vm.helloAngularBeans.message}}</span>
    </div>
…
</html>

Man kann dem Vorgang hier leicht folgen: Man deklariert seine @AngularBean-annotierte CDI Bean und injiziert sie wie jeden regulären AngularJS-Service in den AngularJS-Controller. Aber was ist mit den Anmerkungen in sayHello()? Im Folgenden untersuchen wir sie, eine nach der anderen:

  • @CORS: Cross-origin resource sharing (CORS). Sagt AngularBeans, dass die Methode von einer anderen Domain abgerufen werden kann (ohne einen CORS-Servlet-Filter zu benutzen), was praktisch bei CORDOVA- oder IONIC-Applikationen sein kann.
  • @RealTime: Eine Methode, die mit @RealTime annotiert wurde, folgt dieser Logik:
    • Wenn SockJS nicht genutzt wird, wird AngularBeans WebSockets für Echzeit-Requests nutzen.
    • Wenn SockJS vorhanden ist, wird AngularBeans SockJS entscheiden lassen, welcher Echtzeitmechanismus genutzt wird (WebSockets / polling / long polling…).
  • @NGReturn(model=“message“): Diese Annotation sagt AngularBeans, dass der Return Value der Methode als das Attribute Message in das AngularJS-Modell eingespeist werden soll. In anderen Worten: Der Return Value der Java-Methode wird als „helloAngularBeans.message“ auf der AngulaJS-Serverseite verfügbar sein. In diesem Fall wird der „helloAngularBeans.message“-Wert direkt im Sichtfeld gerendert. Da es sich hier um ein asynchrones Verhalten handelt, wird das Sichtfeld aktualisiert, sobald das Versprechen eingelöst wurde.

Manchmal reicht es allerdings nicht, den Wert der JavaBean direkt ins AngularJS-Modell zu injizieren. Hin und wieder muss man die JavaScript-Funktion aufrufen, um den Return Value der JavaBean-Methode zu prozessieren. In diesem Fall kann man einfach die @NGReturn-Annotation entfernen und folgendes Versprechen für den Controller hinzufügen:

vm.sayHello=function(name){

    helloAngularBeans.sayHello(name).then(function(message){
		 console.log(message);
		 vm.message=message;
});

Und in HTML:

<button ng-click="vm.sayHello(name)"
        class="btn btn-primary">click me
</button>
<span color="blue">message {{vm.message}}</span>

So können wir den Controller als eine Abstraktion zwischen Sichtfeld und „helloAngularBeans“-Service nutzen.

Damit nicht genug!

Es gibt noch viele Dinge zu entdecken, aber um der Länge bzw. Kürze dieses Artikels wegen seien sie hier nur als Aufzählung aufgeführt:

  • AngularBeans unterstützt Method Overloading. Sie können verschiedene Methoden mit identischen Namen, aber unterschiedlichen Parametersignaturen definieren und benutzen, was natürlich nur innerhalb der Grenzen, die JavaScript vorgibt, funktioniert. JavaScript ist eine dynamisch typisierte Sprache, d.h. eigentlich wird nur zwischen der Anzahl der Parameter unterschieden. Methoden mit anderen Parametertypen werden also als identisch angesehen, sofern die Anzahl der Parameter identisch ist – unbesehen des tatsächlichen Typs.
  • Die @NGSessionScoped-Umfangsannotation stellt einen gemeinsamen Kontext zwischen der aktuellen HTTP-Session und der im Browser geöffneten WebSockets-Sessions her. Das kann bspw. nützlich sein, um verschiedene Tabs innerhalb der gleichen Applikation zu unterstützen.
  • Die API-Annotationen der Bean-Validierungen werden in HTML 5-Form-Validierungen übersetzt. Auf der Serverseite annotiert man die Property einfach mit der entsprechenden Annotation aus der Validation-API (JSR303/349) sowie der Annotation @NGModel.
@NGModel
@NotNull
@Size(min = 4, max = 10)
// Bean Validation
public String getFirstName() {
		return firstName;
}

Im HTML reicht es, die ng-form mit einer div plus bean-validierter Direktive zu umschließen:

<div ng-controller="FormCtrl">
  <div class="panel-body" bean-validate>
    <!-- needed to activate bean validation (it must wrap the form) -->
    <div name="registerForm" ng-form>
      <fieldset>
        <label>FirstName </label>
        <input ng-model="firstName" name="firstName" type="text" autofocus />
        <p ng-show="registerForm.firstName.$error.required">required field</p>
        <p ng-show="registerForm.firstName.$error.minlength">min number of characters not respected</p>
        <p ng-show="registerForm.firstName.$error.maxlength">max number of characters not respected</p>
      </fieldset>
    </div>
  </div>
</div>
  • Datei-Uploads triggern Methoden, die mit @FileUpload annotiert sind (und die Parts als Parameter einspeisen)
  • Es gibt einen eingebauten Service, der die Internationalisierung durch Eigenschaftsdateien (ähnlich JSF) fördert
  • Das ModelQuery-Konzept
  • AngularBeans bietet Unterstützung für serverseitige Methoden, die ein Byte Array liefern. Ein typischer Anwendungsfall sind Images. Auf der Client-Seite können diese Byte-Arrays mit ng-src verwendet werden
  • Mit der @Obverse-Annotation der CDI kann man Events auf der AngularJS-Seite auslösen und sie dann auf der Serverseite einfangen
  • Events, die auf der Serverseite ausgelöst oder übertragen werden, können mit $scope.$on(…) abgefangen werden.

Fazit

AngularBeans stellt eine frische, neue Herangehensweise an die professionelle App-Entwicklung dar. Es kombiniert das Beste aus zwei Welten: ein nativer AngularJS-1.4-Client wird von einem ausgewachsenen Java-EE-Backend angetrieben.

Weiterführende Lektüre

Neben einer online verfügbaren Schritt-für-Schritt-Anleitung gibt es in der GitHub-Repository eine Demo-App, die alle Fähigkeiten von AngularBeans ausnutzt.

Dieser Artikel erschien zuerst auf Beyond Java.

Aufmacherbild: Coffee beans in frame on wooden background von Shutterstock / Urheberrecht: Africa Studio

Geschrieben von
Bessem Hmidi
Bessem Hmidi
Bessem ist als Technischer Architekt bei „Business & Decision“ beschäftigt. Er ist Gründer und Chef der „Esprit Tunisian Java user Group“. Seine über neun Jahre Erfahrung mit Java-bezogenen Technologien, insbesondere mit Java EE, setzt er außerdem als professioneller Kursleiter, als Ausbilder und internationaler Speaker ein.
Stephan Rauh
Stephan Rauh
Stephan Rauh arbeitet als Senior Consultant bei der OPITZ CONSULTING Deutschland GmbH. Er befasst sich seit Jahren mit JSF und AngularJS und ist durch seinen Blog sowie seine Open-Source-Projekte AngularFaces und BootsFaces bekannt geworden.
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "AngularBeans: Ein neuer Ansatz für AngularJS und Java EE"

avatar
400
  Subscribe  
Benachrichtige mich zu:
Marco
Gast

Können die AngularBeans auch für Angular 2 genutzt werden?