WebApps– mit JSF 2.2, Primefaces 5, REST, JPA 2, JEE7, JBoss WildFly 10

Die Motivation

In einem Blog-Eintrag wurde bereits eine Beispiel-Applikation mit der JSF 2.2-Implementierung Apache MyFaces besprochen, weshalb hier eine weitere JSF 2-Implementierung, nämlich die Primefaces, besprochen werden sollen und die Migration der Beispielapplikation employeetimetracker von MyFaces auf die Primefaces Applikation employeetimetracker-primefaces erklärt wird. Über die Faces Technologie gibt es bereits einen Blog-Eintrag, worin die Generierung einer JSF 1.2-Applikation auf Basis der JPA-Entities für den JPA-Provider EclipseLink mittels der NetBeans IDE für das Deployment auf dem  GlassFish Application Server beschrieben wurde. Die in diesem Blog-Eintrag mittels JBoss Forge Eclipse PlugIn erstellte und migrierte Applikation employeetimetracker-primefaces wird auf dem JBoss WildFly 10 Final deployt und der Vollständigkeit wegen auch auf dem JBoss WildFly 9 Final und kann ebenso leicht auf weiteren WildFly Servern deployt werden, wie z. B. dem JBoss WildFly 11 Final und JBoss WildFly 12 Final. Dabei ist die Verwendung der beiden JBoss WildFly JEE7-Application Server in einem Blog-Eintrag bereits beschrieben worden und unterstützt den Einsatz der robusten, aber stets eleganten, etablierten Primefaces Frontend-Technologie optimal.

Migration der Beispiel Applikation auf Primefaces

Wie in der pom.xml erkennbar ist, werden hier die folgenden JEE7 Artefakte für den JBoss WildFly verwendet und importiert:

WildFly JBoss Java EE 7 Specification APIs with Tools:
- jboss-javaee-7.0-with-tools

WildFly JBoss Java EE 7 Specification APIs with Resteasy:
- jboss-javaee-7.0-with-resteasy

WildFly JBoss Java EE 7 Specification APIs with Hibernate:
- jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies verwendet:

- jboss-annotations-api_1.1_spec
- jboss-jaxrs-api_2.0_spec
- resteasy-jackson2-provider

- hibernate-jpa-2.1-api
- jboss-ejb-api_3.2_spec
- hibernate-jpamodelgen
- jboss-servlet-api_3.1_spec

Und ebenfalls die CDI 1.1 Dependency:

- cdi-api_1.1

Die Aktivierung von CDI erfolgt bekanntlich mittels beans.xml im WEB-INF Verzeichnis.

Primefaces:

Die Einbindung der erforderlichen Primefaces-UI-Bibliothek primefaces-5.2.jar erfolgt ganz simpel über die entsprechende Maven Primefaces Dependency in der pom.xml. Es ist also im Gegensatz zu einem Ant-Build hier kein extra /lib-Verzeichnis und keine build.xml für einen Ant Build-Prozess mehr erforderlich, denn es wird einfach der folgende Eintrag zur pom.xml für den Maven Build hinzugefügt:

    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>5.2</version>
    </dependency>

Allein durch Änderung dieses Eintrags in der pom.xml kann jederzeit auch auf die gewünschte Primefaces-Version umgestellt werden. Bei der Umstellung der ca. 40 XHTML-Views von MyFaces auf Primefaces sind der Primefaces Showcase sehr hilfreich. Ebenfalls wurde das sehr gute bei DZone erhältliche PDF zur JSTL hier verwendet. Durch die Verwendung von CDI 1.1 haben die JSF ManagedBeans stattdessen die Annotation @Named und bleiben, bis auf ein paar Helper-Methoden zur Ausgabeformatierung, nahezu identisch zu den ManagedBeans der  MyFaces-Applikation employeetimetracker. Auch gibt es noch ein paar zusätzliche toString()-Methoden auf  einigen JPA-Entities, die bei Bedarf eine geänderte String-Repräsentation der Attribute liefern können.

forge.taglib.xml:

Die in den Beispiel-Applikationen verwendete, moderne Forge TagLib kann sehr einfach erweitert werden, indem eine zusätzlich benötigte Methode in der ViewUtils-Klasse ergänzt wird und deren Signatur in der forge.taglib.xml im entsprechenden Verzeichnis bekanntgemacht wird. Die TagLib wird in der entsprechenden .xhtml-View über den korrekten Eintrag mit dem richtigen Prefix im Document-Header eingebunden und mittels forgetaglib-Scope mittels Expression Language (EL) an der gewünschten Stelle mit den richtigen Input-Parametern aufgerufen. Hier die forge.taglib.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">
    <namespace>http://jboss.org/forge/view</namespace>

    <function>
    	<function-name>asList</function-name>
    	<function-class>de.binaris.employeetimetracker.view.ViewUtils</function-class>
    	<function-signature>
    		java.util.List asList(java.util.Collection)
    	</function-signature>
    </function>
    
    <function>
        <function-name>display</function-name>
        <function-class>de.binaris.employeetimetracker.view.ViewUtils</function-class>
        <function-signature>
            java.lang.String display(java.lang.Object)
        </function-signature>
    </function>
	
    <function>
    	<function-name>count</function-name>
    	<function-class>de.binaris.employeetimetracker.view.ViewUtils</function-class>
    	<function-signature>
    		int count(java.util.Collection)
    	</function-signature>
    </function>
	
    <function>
    	<function-name>listExcludingSelectedOne</function-name>
    	<function-class>de.binaris.employeetimetracker.view.ViewUtils</function-class>
    	<function-signature>
    		java.util.List listExcludingSelectedOne(java.util.Collection,java.lang.Object)
    	</function-signature>
    </function>    
</facelet-taglib>

Hier auch die ViewUtils-Klasse unter Verwendung generischer Typen T und Collections List<T>:

// ViewUtils:

package de.binaris.employeetimetracker.view;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.Id;

/**
 * Utilities for working with Java Server Faces views.
 */
public final class ViewUtils {

   public static <T> List<T> asList(Collection<T> collection) {

      if (collection == null) {
         return null;
      }
      return new ArrayList<T>(collection);
   }

   public static String display(Object object) {
      if (object == null) {
         return null;
      }
      try {
         // Invoke toString if declared in the class. If not found, 
         // the NoSuchMethodException is caught and handled
         object.getClass().getDeclaredMethod("toString");
         return object.toString();
      }
      catch (NoSuchMethodException noMethodEx) {
         try {
            for (Field field : object.getClass().getDeclaredFields()) {
               // Find the primary key field and display it
               if (field.getAnnotation(Id.class) != null) {
                  // Find a matching getter and invoke it to display the key
                  for (Method method : object.getClass().getDeclaredMethods()) {
                     if (method.equals(new PropertyDescriptor(field.getName(),                     
                                          object.getClass()).getReadMethod()))
                     {
                        return method.invoke(object).toString();
                     }
                  }
               }
            }
            for (Method method : object.getClass().getDeclaredMethods()) {
               // Find the primary key as a property instead of a field, and display it
               if (method.getAnnotation(Id.class) != null) {
                  return method.invoke(object).toString();
               }
            }
         } catch (Exception ex) {
            // Unlikely, but abort and stop view generation if any exception is thrown
            throw new RuntimeException(ex);
         }
      }
      return null;
   }

   public static <T> int count(Collection<T> collection) {
      if (collection == null) {
               return 0;
      }
      return collection.size();
   }

   public static <T> List<T> listExcludingSelectedOne(Collection<T> collection, T selected) {
      if (collection == null) {
         return null;
      }
      if (selected == null) {
        return new ArrayList<T>(collection);
      }
      List<T> list = new ArrayList<T>(collection);
      list.remove(selected);
      return list;
   }

   private ViewUtils() {
      // Can never be called, only by getInstance - Singleton pattern
   }
}

Über Tags, TagLibs und die Erweiterung bestehender und die Definition eigener Tags gibt es bereits einen Blog-Eintrag und durch die soeben gezeigten Beispiele ist erkennbar, wie leicht es ist, eigene Tags für die .xhtml-Views heutiger Primefaces-Applikationen zu definieren.

Die Konfiguration des JBoss WildFly Application Servers 11 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2, 9 und 10 Final und betrifft die Datasource, die MySQL-Datenbank Inno DB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle ein wichtiges Thema, welches in diesem Blog bereits in einem früheren Blog-Eintrag beschrieben wurde. Die Anbindung an die relationale  Datenbank MySQL 5 erfolgt übrigens analog für die JBoss Application Server WildFly 8.1, 8.2, 9, 10 und WildFly 11.

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, wie in diesem Blog-Eintrag hier beschrieben wurde, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt und auch der Pfad für das log-Dateien Verzeichnis in der Datei [server]/standalone/configuration/logging.properties.

Hier auch der WildFly 11 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Minimale Voraussetzung ist jeweils Java 8. Der fertig konfigurierte WildFly 11 mit vielen weiteren Beispiel-Applikationen ist hier zum Download verfügbar, kann runtergeladen, entpackt und gestartet werden.

Auch hier ist nur der Pfad des JAVA_HOME anzupassen, damit dieser auf das tatsächliche Open JDK – Verzeichnis des Server-Systems zeigt und ebenfalls der Pfad für das log-Dateien Verzeichnis in der logging.properties.

Die Entity-Relationship Diagramme, welche als Vorlagen der Beispiel-Applikation verwendet wurden (TimeTracking, Project Tracking) können in der Primefaces Beispielapplikation hier nachgesehen werden.

Die Beispiel-Applikation kann auch selbst erneut deployt werden, ohne den bereits gestarteten WildFly Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server

– ins Server-Unterverzeichnis /standalone/deployments das Archiv employeetimetracker-primefaces.war speichern

Sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann entweder hier oder unter der folgenden Url aufgerufen werden:

http://localhost:8080/employeetimetracker-primefaces

Die Blog-Einträge können übrigens hier nach Stichworten und Erscheinungsmonat leicht durchsucht werden.

Somit besteht durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces 6, Angular JS und Bootstrap, sowie den die JEE7-Spezifikation oder JEE8-Spezifikation implementierenden WebService-und Backend-Technologien folgen. Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test-Driven Development mit Java, sowie einen schönen Frühling.

Architekturen mit Spring CDI, JPA, JBoss, JavaEE

 Die Motivation

Spring und Java EE finden sich in sehr vielen wichtigen und erfolgreichen Kunden-Projekten als Frameworks bzw. Technologien mit vielen erweiternden unterstützenden Frameworks wieder. In vielen Foren und Blogs werden sie bereits bewertet oder sogar miteinander verglichen, weshalb beide Technologien sich auch hier im selben Blog-Eintrag wiederfinden, um sie zwar gegenüberzustellen, die Bewertung jedoch den jeweiligen Projektbeteiligten zu überlassen. In einem bereits erfolgreich laufenden Projekt wissen diejenigen, die es planen und umsetzen meistens selbst, welche Technologie für sie am besten geeignet ist. Für neue Software-Projekte geben die vorhandene, operative Infrastruktur und die planenden Architekten und umsetzenden Entwickler oftmals mit den entscheidenden Hinweis zur Auswahl der Projekt-optimalen Architektur/Technologie. D.h. wer bereits Spring kann, am Start hat, erfolgreich einsetzt, macht weiterhin Spring, wer bereits Java EE kann, am Start hat, erfolgreich einsetzt, macht weiterhin Java EE, z. B. mit dem sehr empfehlenswerten JBoss Application Server und den modernen JEE7/JEE8 JBoss WildFly Application Servern.

Die Grundprinzipien

In Spring kann man sich alles von den PoJo-JavaBeans bis zu den DAOs, Facaden, Services, DB- oder Front-Controllern, Transaktionsbehandlung, View-Beans etc. für die Dependency Injection (DI) zur Start- und Aufrufzeit definieren und in applicationContext-bezogenen XML Config-Dateien bzw. per Annotationen konfigurieren. In Java EE 6 und CDI (hier am Beispiel JBoss AS 6 und 7) gibt es viele dieser Klassen bereits, sie brauchen nur mittels Annotationen deklarativ annotiert zu werden (auch EJBs der Versionen 3.x). Vorausgesetzt die Funktionalität bzw. der Service ist per XML-Datei bereits aktiviert oder auf diese Weise für alle deployten Applikationen dieser Server-Instanz konfiguriert und damit nach dem Serverstart verfügbar.

Die Konfigurationsdateien innerhalb einer WAR-Datei

Spring hat verschiedene /WEB-INF/applicationContext.xml Config-Dateien und Java EE hat auch bestimmte Config-Dateien für jede API-Implementierung, die auch innerhalb einer WAR-Datei mit deployt werden müssen. Einige dieser Java EE Config-Dateien API sind unbedingt erforderlich, aber nicht alle. Hier eine Übersicht der Basiskonfigurationen. Zu beachten; Wenn diese Dateien im falschen Verzeichnis deployt werden, wird das Gesamtsystem meist nicht die gewünschte Funktionalität bieten:

Java EE API Config-Datei Verzeichnis Erforderlich für Funktion
Java Persistence API /META-INF/ persistence.xml Ja
Enterprise Java Beans WAR /WEB-INF/ ejb-jar.xml Nein
Java Server Faces /WEB-INF/ faces-config.xml Nein
Contexts & Dependency Injection /WEB-INF/ beans.xml Ja
Web Configuration /WEB-INF/ web.xml Nein
JAX-RS (REST Web Services) /WEB-INF/ web.xml Nein

Die meisten dieser Config-Dateien benötigen ein bestimmtes XML-Schema, bzw. einen XML Header, der  dem Application Server (z. B. JBoss Application Server oder GlassFish) mitteilt, welche Version der Technology to verwendet wird, wobei die meisten dieser APIs dabei zu früheren Versionen kompatibel sind. Ganz ähnlich verhält es sich mit neuen XML Schemas innerhalb Springs XML Headern in den applicationContext.xml Config-Dateien.

EJBs können also einfach über Annotationen definiert werden und benötigen keine weitere Config-Datei, um sie zu enablen, ebenso RESTful WebServices mit JAX-RS, wenn ein mindestens Java EE 6  zertifizierter Application Server, wie JBoss Application Server 6 oder 7 oder WildFly 8.1 verwendet wird. Alles kann mit Annotationen deklarativ definiert werden, sobald es einmal, z. B. in der web.xml enabled wurde. Allerdings ist die beans.xml erforderlich, um Java EE 6 (CDI) zu enablen.

Die Konfigurationsdateien für Module in einem .JAR

Eine sehr gute Eigenschaft von Java EE APIs ist die Möglichkeit, compilierte Klassen und Sourcecode einer Application in mehrere wiederverwendbare JAR-Dateien aufzuteilen, wobei jedes einzelne JAR seine eigene Konfiguration und die Klassen/den Sourcecode für das Gesamtsystem enthält, in welches sie includiert werden. Wenn z. B. mehrere auf einem Application Server deployte Applikationen alle  denselben DataAccessProvider verwenden, würde man z. B. ein “shared domain-model” JAR Archiv erzeugen und in jede Applikation includieren, die genau diesen Data Access Provider verwenden  soll. Die Konfiguration des Data Access Providers ist dann komplett in diesem JAR enthalten und geschieht unter Verwendung genau der folgenden Config-Dateien im META-INF Verzeichnis des JARs:

Java EE API Config-Datei Verzeichnis Erforderlich für korrekte Funk.
Java Persistence API /META-INF/ persistence.xml WAR benötigt eine Config-Dat.
Enterprise Java Beans WAR /META-INF/ ejb-jar.xml Nein
Java ServerFaces /META-INF/ faces-config.xml Nein
Contexts & Dependency Injection /META-INF/ beans.xml Ja
Web Config. /META-INF/ web- fragment. xml Nein
JAX-RS (REST Web Services) /META-INF/ web- fragment.xml Nein

Hierbei unterscheidet sich der Config-Dateiname web-fragment.xml von dem Config-Dateinamen web.xml aus dem WAR. Das WAR benötigt auch dann weiterhin eine eigene persistence.xml

Die Application Server Konfiguration

Zusätzlich zur Applikations-Konfiguration ist es in Java EE sinnvoll und erforderlich, den Application Server zu kennen und möglichst optimal zu konfigurieren. Leichtgewichtige JEE Applikationen mit Spring und Hibernate laufen hingegen auf allen Application Servern meist im Tomcat Mode, da hierbei die Application Server Konfiguration durch die Anwendungs-Konfiguration erfolgt, das bedeutet dasselbe WAR für alle Application Server bei der Verwendung von Spring. Wenn man die Vorteile des jeweiligen Application Server nutzen will (z. B. JBoss Application Server oder GlassFish) und diesen konfiguriert hat (z. B. für den Einsatz einer bestimmten Datasource mit dem JBoss-eigenen Hibernate), erfolgt dann die Java EE Konfiguration der Applikation. Z.B. ist nach der Konfiguration des Transactional-Features des Java EE Application Servers, um z. B. JPA mit Transaktionen zu verwenden nur noch erforderlich, die jeweilige Java-Klasse mit der @Transactional Annotation zu annotieren. Die Java EE Config-Dateien sollten also verwendet werden, um Einstellungen für die korrekte Konfigurationsweise der Applikation festzulegen und funktionieren als natürliche Erweiterungen der Java EE Modul Config-Dateien.

Die JBoss Application Server Dokumentation findet sich hier:

Container Config-Datei
JBoss AS 6 –Allgemeine Config-Datei /WEB-INF/jboss-web.xml
JBoss AS 6 –Data Source Config-Datei ${JBOSS_HOME}/server/default/deploy/*-ds.xml (im aktuellen  JBoss Server Deploy Verzeichnis, oder innerhalb eines .ear-Archivs einer Applikation)
Sun GlassFish v3 AS – Allgemeine Config-Datei /WEB-INF/sun-web.xml
Sun GlassFish v3 AS – Data Source Config-Datei sun-resources.xml mit Definition des jdbc-connection-pool und der jdbc-resource mittels web-admin console anlegen und speichern.

Die Definition einer transaktionalen Datasource erfolgt also beim JBoss AS 6 in der entsprechenden  „*-ds.xml“ und beim GlassFish v3 in der sun-resources.xml, z. B. über die Web-Admin Console, für eine Standalone-Application Datasource-Konfiguration mit Hilfe der genannten Config-Dateien, wodurch die weitere manuelle Konfiguration des jeweiligen Application Servers vermieden wird. Die entsprechenden Konfigurationen (Datenbank User/Passwort, Datasources, JMS-Queues) können beim JBoss aber auch einmal pro Server erfolgen (z.B. beim JBoss AS 7 in der /standalone/standalone.xml).

Die Context und Dependency Injection (CDI) Konfiguration

Spring hat die @Autowired Annotation, Java EE 6 (CDI) hat die @Inject Annotation. Seit Java EE 6 CDI ist jede Klasse in einem WAR- oder JAR-Archiv, das eine beans.xml Config-Datei enthält,  automatisch eine injectbare Bean gemäß CDI und kann somit unter Verwendung eines Scopes (definiert Sichtbarkeit und Lebensdauer) mittels Dependency Injection verwendet werden. Genauso wie die definierten Beans innerhalb der applicationContext.xml einer Spring Applikation.

Voraussetzung zum Starten des CDI Containers ist also eine vorhandene (evtl. leere) /WEB-INF/beans.xml. Technisch gesehen können natürlich nur die Beans eines Archivs (mit beans.xml) injected werden. Mithilfe einer Producer Methode können auch Klassen ausserhalb von Bean Archiven instanziiert und zu CDI Beans werden, auch mit Hilfe von Seams XML Definitionen können die definierten Beans mit Hilfe von Bean-Factories und Producer-Methoden zu CDI Beans werden. Auch Spring verwendet intern eine BeanFactory zur Instanziierung der definierten/annotierten Spring Beans, nämlich die Apache (Commons) BeanFactory.

Für das Log-In und die User Authentifizierung wird auch gerne ein Framework wie Seam Security verwendet. In Spring wird auch gerne das Spring Security Framework verwendet. Beispiel-Code zum Log-In unter Verwendung der @SessionScoped Annotation findet sich auch in diesem Blog-Eintrag hier. Es gibt im Java EE die folgenden vorhandenen Scopes, die im JBoss Verwendung finden:

Annotation Lebensdauer Context Object
@RequestScoped Vom Begin bis zum Ende eines HTTP Requests. HttpServletRequest
@ConversationScoped Beginnt, wenn der Request beginnt und endet, wenn der Request endet und die Conversation keine lang-laufende Conversation ist. Der Call  conversation.begin() bedeutet, dass die Conversation auch zusätzliche Requests verarbeiten können soll. Calling conversation.end() bedeutet, dass die Conversation enden soll, wenn der aktuelle Request endet. Unabhängig davon gilt: wenn die Session jedoch beendet wird, werden alle aktiven Conversations ebenso beendet. HttpSession
@SessionScoped Vom ersten beginnenden Request, wenn die  httpServletRequest.getSession()-Methode aufgerufen wird, bis zum letzten Request, wenn httpSession.invalidate() aufgerufen wird. HttpSession
@ApplicationScoped Für die gesamte Lebensdauer der Applikation. Vom Zeitpunkt nach dem Server Start mit der deployten Applikation, bis kurz vor dem Server-Shutdown. ServletContext
@Dependent (default) Kontrolliert vom Objekt, in dass es mittels @Injected injected wurde. Wenn es in eine @SessionScoped Bean @Injected wurde, dann wird die @Dependent Bean ebenso eine @SessionScoped Bean. Das bedeutet, dass es mehrere separate Instanzen derselben @Dependent Bean geben kann,  von denen einige @RequestScoped sind, oder irgendeinen anderen Scope haben. Dies ist dem Scope @Prototype in Spring sehr ähnlich. *** Diejenige Bean, in welche diese Bean injectet wurde.

Weitere Custom Scopes können bei Bedarf erstellt werden. Wenn eine injectete Bean allerdings Scope-bedingt einen kürzeren Lebenzyklus hat als ihre Consumer Bean, d.h. die Bean, in die sie injectet wird, muss dies bei der Programmierung der Ablauf- und Business-Logik beachtet werden, wie im folgenden Beispiel:

@ApplicationScoped
public class AuthorizationBean {

    @Inject
    private UserCredentialsBean credentials;
}

Hierbei wird eine @SessionScoped Bean in eine @ApplicationScoped Bean injectet, die normalerweise einen längeren Lebenszyklus hat, worauf im Programmablauf geachtet werden sollte, um Runtime Exceptions zu vermeiden. In Spring gibt es solche Effekte auch, wenn eine Consumer Bean auf eine noch nicht geladene injectete Bean zugreifen will und dabei längere Wartezeiten in Kauf nehmen muss, bis die injectete Bean allokiert und möglichst fertig initialisiert ist. Es besteht dabei die Möglichkeit, dass es z.B. noch keine aktive UserSession gibt, wenn die AuthorizationBean erzeugt wird und der Container somit noch nicht in der Lage war, die UserCredentialsBean-Dependency zu erfüllen, was aber durch entsprechende Konfiguration umgestellt werden kann, um auf die Erfüllung aller Dependencies zu warten, es wird aber wegen der längeren Loadzeiten seltener verwendet. Generell wrappt Spring die Objekte der verschiedenen Scopes mit Proxy-Objekten, die sofort injectet werden können. Zur Startzeit wird der ApplicationScoped Bean somit ein Proxy Objekt injected, welches die gewrappte SessionScoped Bean aufrufen kann. Beim Zugriff auf das UserCredentialsBean Object gibt es nur ausserhalb seines UserSession-Scopes dann eine Exception zur Laufzeit.

In CDI weiß der Container, dass zu diesem Zeitpunkt im Lifecycle der Anwendung noch kein  @SessionScoped Object verfügbar ist und wartet mit dem Zugriff auf die Credentials, bis eine Instanz der UserCredentialsBean allokiert werden kann und verfügbar ist. Beim Zugriff auf das UserCredentialsBean Object gibt es nur ausserhalb seines UserSession-Scopes auch eine Exception, weshalb ein solcher Zugriff im Programm durch entsprechend intelligente Implementierung der Use Cases vermieden werden muss. Java EE und Spring verhalten sich also auch hier nahezu gleich.

Wenn in Spring die @ApplicationScoped AuthorizationBean unter Verwendung der @SessionScoped UserCredentialsBean erzeugt wird, ist die injectete Instanz wegen des in Spring implizit verwendeten Singleton Patterns für die gesamte Lebensdauer der Consumer Bean dieselbe UserCredentialsBean. Dieselbe UserCredentialsBean wird also für alle Aufrufe innerhalb der AuthorizationBean verwendet, weshalb die AuthorizationBean mittels AOP-Annotationen (AspectJ, SpringAOP) als “Dynamischer  Proxy” mit entsprechenden JoinPoints und Pointcuts verwendet werden sollte, entweder innerhalb der Spring Configuration oder mittels Annotationen auf den entsprechenden Klassen.

In CDI weiß der Container, dass eine @SessionScoped Bean normalerweise keine so lange Lebensdauer hat, wie eine @ApplicationScoped Bean und dass es mehr als eine aktive UserSession geben kann. CDI wird die passende @SessionScoped UserCredentialsBean finden und bei auszuführenden Operationen auf der Consumer Bean verwenden bzw. neu allokieren (@Inject), damit immer ein passendes UserCredentialsBean Objekt verwendet werden kann. Allgemein hat CDI ebenfalls sehr viele Annotationen (z. B. @Model, @Service und @Repository) und lässt es auch zu, eigene Stereotype Annotationen zu definieren, um die Funktionalität zu erweitern und hat selbst analog der @Interceptor Annotation viele Erweiterungsmöglichkeiten. Dadurch ist CDI ein etwas größerer, schwererer DI Container als Spring, unterstützt aber denselben typsicheren Ansatz wie Spring und ist genau wie Spring extrem erweiterbar.

Direktzugriff auf Beans innerhalb einer Applikation

Versucht man, ohne Verwendung von Dependency Injection (also ohne @Inject in CDI bzw. ohne  @Autowired in Spring zu verwenden), direkt eine Referenz auf eine Bean zu erhalten, aus der aktuell arbeitenden Software-Komponente der Applikation, so ist es manchmal erforderlich, das Framework explizit Programmier-technisch nach einer Bean Instanz zu fragen, um diese zu erhalten.

In Spring ist die Instanz einer Bean mittels Java API-Implementierungen leicht erhältlich, vorausgesetzt die richtigen Listener wurden in der web.xml Konfiguration deklariert. Dann funktioniert der Bean Lookup aus dem ApplicationContext einfach: MyBean bean = ApplicationContext.getBean(“myBean”); Die CDI Entsprechung zum ApplicationContext ist der BeanManager, auf den z. B. per JNDI zugegriffen werden kann, oder durch den Einsatz des“DeltaSpike” (“JBoss Solder”) Projekts, welches eine statische BeanManagerAccessor.getBeanManager() Methode bietet (ähnlich wie Springs WebApplicationContext-Utility Klasse). Der BeanManager ist per DeltaSpike mit folgender Methode erhältlich:

public BeanManager getBeanManager() {
    return BeanManagerAccessor.getManager();
}

Es gibt in der Basisklasse BeanManagerAware weiterhin die Function getBeanManager(), die einen JNDI Zugriff auf den BeanManager ermöglicht, wie im folgenden Beispielhaft gezeigt wird. Zu den Best Practices gehört jedoch bevorzugt der bereits gezeigte DeltaSpike-Aufruf, wenn der BeanManager nicht direkt über @Inject BeanManager erhältlich ist. Hier die anderen Möglichkeiten:

a) Aufruf des BeanManagers innerhalb eines JSF Requests aus dem ServletContext: (Dies funktioniert mit den meisten CDI Implementierungen.)

public BeanManager getBeanManager() {
    return (BeanManager) ((ServletContext) 
        facesContext.getExternalContext().getContext()).getAttribute("javax.enterprise.inject.spi.BeanManager");
}

b) Aufruf des BeanManagers innerhalb eines Web ServletRequests aus dem ServletContext:

public BeanManager getBeanManager(HttpServletRequest request) {
    return (BeanManager) request.getSession().getServletContext().getAttribute("javax.enterprise.inject.spi.BeanManager");
}

c) Aufruf des BeanManagers mittels JNDI, ohne DeltaSpike (kein Web Request erforderlich)

public BeanManager getBeanManager() {
    try {
        InitialContext initialContext = 
           new InitialContext();
        return (BeanManager)initialContext
               .lookup("java:comp/BeanManager");
    catch (NamingException e) {
        log.error(
            "Couldn't get BeanManager through JNDI");
        return null;
    }
}

Instanziierung einer Bean mittels BeanManager

Sobald man den BeanManager hat, ist vom Container eine Instanz der gewünschten Bean erhältlich.

Der folgende Code mit den Methodenaufrufen ist bereits in der Klasse WeldX verfügbar, oder kann in einer eigenen Utility-Klasse gekapselt werden.

Erhalten einer Bean Instanz aus dem CreationalContext:

@SuppressWarnings("unchecked")
public static <T> T getContextualInstance(final BeanManager manager, final Class<T> type) {

    T result = null;
    Bean<T> bean = (Bean<T>) manager 
                     .resolve(manager.getBeans(type));
    if (bean != null) {
        CreationalContext<T> context = manager
               .createCreationalContext(bean);
        if (context != null) {
            result = (T) manager.getReference(
                         bean, type, context);
        }
    }
    return result;
}

Aufräumen von Beans des @Dependent Scopes und Beans vom BeanManager

  • Aufräumen von Bean Scopes: In CDI gibt es das Konzept einer Bean mit @Dependent Scope. Eine solche Bean übernimmt den Scope der Bean, in die sie injectet wurde. Das bedeutet dann, wenn man mit Hilfe von Java APIs eine Instanz einer Bean mit @Dependent Scope erhält, dass diese Bean nicht in einem Context Objekt (Request, Session, Application, oder einem Custom Scope) gespeichert wird und auch nicht erneut aus einem solchen Context Objekt geholt werden kann. Mit anderen Worten, @PostConstruct Methoden werden zwar aufgerufen, wenn die Bean erzeugt wird, da es aber für CDI keine Möglichkeit gibt, zu erfahren, wann die Bean wieder zerstört wird (da sie nicht in einem Context gespeichert wird, der normalerweise CDI benachrichtigen würde, wann es Zeit ist, die Bean wegzuräumen) können auch @PreDestroy annotierte Methoden auf der Bean nicht automatisch aufgerufen werden. Diese Methoden können aber explizit für die Wegräumarbeit aufgerufen werden.
  • Aufräumen von über den BeanManager erhaltenen Beans: Hierbei ist das CreationalContext  Objekt, welches innerhalb der Methode getContextualInstance erzeugt und verwendet wird, bevor  eine Referenz der gesuchten Bean erhältlich ist, dasjenige Objekt, welches verwendet werden  muss, um eine Bean wegzuräumen, d. h. die entsprechende @PreDestroy Methode aufzurufen.  (Die Method getContextualInstance verwendet jedoch nur eine lokale Referenz auf das  CreationalContext Objekt, weshalb die @PreDestroy Methode auf über den BeanManager erhaltenen Beans nicht automatisch aufgerufen werden kann.) Wenn man also eine Bean über den BeanManager erhalten möchte, sollte man zuvor eine Lösung erarbeiten zum Wegräumen mittels CreationalContext Objekt. Genau wie beim Aufräumen von Bean Scopes können in diesem Fall die mit @PreDestroy annotierte Methoden auf der Bean nämlich nur explizit aufgerufen werden.

Zugriff auf Beans mit Custom Scopes

Die beste Art mit Instanzen von Objekten umzugehen, ist immer, diese Bean via Injection mittels der  @Inject Annotation in CDI verfügbar zu machen und nicht über Java APIs.

Oft kann das Problem wie im Seam Faces Module behoben werden, wo für Java Server Faces (JSF) Injection-Unterstützung in User-Objekte jederzeit verfügbar ist, wie z. B. von Validatoren oder  von Convertern. Der Einsatz von Seam hilft dabei genauso in Seam Wicket, Seam Servlet, Seam JMS, und anderen Seam Modulen, von denen viele aus den Seam3 Modulen als CDI-Erweiterungen in das Apache DeltaSpike-Projekt übergegangen sind. Hier eine Übersichtstabelle:

Seam3 Projekt Neues Projekt
Solder Apache DeltaSpike
Seam Catch Apache DeltaSpike
Seam Config Apache Aries
Seam Cron TBD
Seam Drools Drools Project
Seam Faces Apache DeltaSpike and JSF 2.2
Seam Errai Errai
Seam International Apache DeltaSpike
Seam JCR ModeShape
Seam JMS JSR 343
Seam Mail cdi-mail
Seam Persistence Apache DeltaSpike and JTA 1.1
Seam Remoting Deprecated
Seam Reports TBD
Seam Rest RESTEasy
Seam Security PicketLink
Seam Servlet CDI 1.1
Seam Social Agorava
Seam Spring Proposed for Apache DeltaSpike
Seam Validation Bean Validation 1.1
Seam Wicket Apache Wicket
PDF and Spreadsheets TBD

Injection-Unterstützung hinzuzufügen bedeutet dabei manchmal auch einen Custom Scope hinzuzufügen, was sich kompliziert anhört, aber meistens genauso simple ist, wie einen bereits existierenden Bean Scope (Request, Session, Application, Conversation) an ein bereits existierendes Context Objekt zu binden, wie die HttpSession an den @SessionScoped, oder in JSF UIViewRoot an den @ViewScoped in Seam Faces.

JPA, CDI, Spring, Hibernate

In Spring wird die DataSource im applicationContext.xml definiert, danach der Connection Pool, dann Hibernate, das den Connection Pool als DataSource verwenden soll, dann der HibernateTransactionManager. Dann wurde eine Byte-Code Assist Library (AOP) initialisiert, um Transaktionen über Bean-Grenzen hinweg und Security-Restriktionen per Annotationen zu ermöglichen.

Der PersistenceContext wird in Spring in eine Adapter-Klasse gewrappt. In Java EE kann der “Extended“  PersistenceContext verwendet werden, der darüber entscheidet, wie das ORM Model funktionieren soll. Denn dafür sind sowohl die Entities als auch der PersistenceContext wichtig.

In Java EE benötigt man nur die /META-INF/persistence.xml. Ein Beispiel dazu für den JBoss 6 oder JBoss 7 (hier erhältlich: http://www.jboss.org/jbossas/downloads/) oder für einen GlassFish mit bereits installiertem Hibernate (hier gibt es weitere Informationen dazu: https://blogs.oracle.com/GlassFishPersistence/entry/use_hibernate_as_a_persistence mit einer simplen Beispiel persistence.xml https://blogs.oracle.com/GlassFishPersistence/resource/persistence.xml ) gibt es hier:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
	http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0">	
    <persistence-unit name="example">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:/DefaultDS</jta-data-source> 
        <!-- Data source for GlassFish instead of JBoss AS 6 or 7
        <jta-data-source> jdbc/__default </jta-data-source> -->
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <!-- Properties for Hibernate (default provider for JBoss AS, 
                 must be installed on GlassFish or packaged into EAR) -->
	  <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
	  <property name="hibernate.show_sql" value="true"/>
	  <property name="hibernate.format_sql" value="false"/>
        </properties>
    </persistence-unit>
</persistence>

Natürlich gibt es für den GlassFish auch die Möglichkeit, die jeweils gewünschte Hibernate-Version mit der Applikation in einem EAR-Archiv zu deployen, anstatt Hibernate im GlassFish zu deployen, worüber es hier einen sehr guten Blog-Eintrag gibt: http://javafromthetrenches.wordpress.com/2011/01/15/using-hibernate-jpa-with-glassfish/

Die neuste Hibernate Version von RedHat ist hier erhältlich: http://hibernate.org/orm/downloads/

Eine kurze Information zu den Transaktionen. Diese funktionieren bei Verwendung von JTA  DataSources auch gemäß dem JTA-Standard:

JTA Transaktionen sind somit auch nie an eine Scoped Annotation gebunden. Eine JTA Transaktion beginnt mit der ersten EJB Methode, die mit dem Required Attribut (default) oder mit dem RequiresNew Attribut versehen wurde. Die Transaktion wird dann automatisch commitet, sobald diese Methode beendet wurde.

Also werden Transaktionen völlig unabhängig vom Scope der Bean (@RequestScoped or @SessionScoped) immer commitet (und der PersistenceContext flushed()) am Ende eines Requests. Dies war bereits in EJB 2.0 so und bleibt gleich in EJB 3.1 denn der Einsatz von CDI ändert nicht die JTA Semantik.

JTA Container Managed Transaktionen (CMT) haben immer Request Scope, was auch sehr sinnvoll ist, denn niemand möchte eine 30 Minuten (HttpSession Timeout, z.B. konfiguriert in der web.xml) lange Transaktionen und auch keine Conversation Scope lang laufende Transaktionen. Transaktionen sollten immer so kurz wie irgendwie möglich sein, um möglichst wenig Speicher des relationalen Datenbank-Systems zu benötigen.

Die DataSource kann in der JBoss Application Server Konfiguration konfiguriert werden. DataSources können aber auch für jede Applikation einzeln definiert werden, wie hier beschrieben ist: http://docs.jboss.org/jbossas/docs/Installation_And_Getting_Started_Guide/5/html/Using_other_Databases.html

Dann sollte man nur darauf achten, die Datenbank-Zugangsdaten ins Sourcecode-Verwaltungssystem nicht miteinzuchecken, oder zumindest nicht im Klartext.

Eine allgemeine, sehr hilfreiche Dokumentation zum JBoss und zu JBoss Seam (Seam Faces mit .xhtml-Views) ist hier zu finden: http://docs.jboss.org/jbossas/docs/Installation_And_Getting_Started_Guide/5/html/

Über das Erstellen von JBoss Seam Applikationen mit Hibernate, JPA-Entities mittels JBoss Developer Studio gibt es bereits zahlreiche Beispiel-Projekte.

Einer der vielen Vorteile bei der Verwendung von JBoss Seam ist das “Seam JPA Persistence“  Module, mit dem JPA und PersistenceContext in Standard CDI Beans transaktional ohne EJBs  verwendet werden können. Seam3 bietet dabei auch Unterstützung für Managed Transactions, Security, Remoting, und Messaging, bietet also einen einheitlichen Programmieransatz basierend auf CDI für Java EE, weshalb Seam 2 und 3 sehr erfreuliche Entwicklungen und hervorragende Frameworks sind, genauso wie der der in vielen professionellen IT-Projekten anzutreffende JBoss Application Server.

Zu Object Relational Mappings, Hibernate und dem EntityManager (hier die Dokumentation: http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/index.html), gibt es hier mehr Informationen auf jboss.org (http://docs.jboss.org/hibernate/stable/). Hibernate verwendet als JPA-Implementierung auch dieselben Annotationen wie JPA, deshalb hier auch der Link zur Dokumenattion: http://docs.jboss.org/hibernate/stable/annotations/reference/en/pdf/

Über Hibernate und JPA gibt es bereits einige Blog-Einträge.

Doch nun zu einem EJB-Beispiel. Hier eine EJB mit Persistence, die noch nichts macht:

@Stateful
public class Service
{
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;
}

Eine EJB, die JPA Entities in einer Datenbank anlegt(C)/aktualisiert(U)/löscht(D)/findet(R), also die typischen C-R-U-D-Operationen ausführt):

@Stateful
public class Service {
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    public <T> void create(final T entity) {
        em.persist(entity);
    }

    public <T> void delete(final T entity) {
        em.remove(entity);
    }

    public <T> T findById(final Class<T> type, final Long id) {
        Class<?> clazz = getObjectClass(type);
        return (T) em.find(clazz, id);
    }

    public <T> void save(final T entity) {
        em.merge(entity);
    }
}

Schon ist die Service-Klasse für Datenbank-Zugriffe und JPA-Entities fertig. Ganz ähnlich generische Stateless DAOs finden sich auch im Seam Persistence Source hier. Weitere interessante Beispiel-Projekte finden sich z. B. auch hier (http://ocpsoft.org/wp-content/uploads/), von ocpsoft stammt auch der Original Blog hier, worauf dieser Blog-Eintrag basiert. Ein großes Dankeschön an die JBoss Rockstars!

Persistenz, Transaktionen und der Conversational Scope

Eine LazyInitializationException tritt z. B. in einer JPA-Implementierung auf, wenn abhängige Objekte (Adressen einer Person) implizit nachgeladen werden sollen, obwohl die Person nicht mehr “managed“ ist, d.h. nicht mehr über den PesistenceContext erhältlich ist, sondern der PersistenceContext schon geschlossen/weggeräumt und die Datenbank-Connection geschlossen wurde.

Das Persistenz-Subsystem benötigt entweder eine Session (Hibernate) oder einen PersistenceContext mit einem EntityManager (JPA), der zum Öffnen von Connections, der Rückgabe von Ergebnislisten und der Entscheidung, ob Objekte geändert wurden oder nicht, gebraucht wird, um zu entscheiden, ob diese Objekte in der Datenbank zurückgespeichert werden sollen oder nicht. Im Fall der LazyInitializationException lautet die entsprechende Fehlermeldung dann z. B. LazyInitializationException – “Entity is not associated with a known PersistenceContext”  

Wenn der PersistenceContext jedoch ausserhalb der Transaktion zerstört wird, gehen alle ungespeicherten Änderungen in diesem PersistenceContext verloren und können nicht mehr in der DB gespeichert werden.

Das Speichern und Committen der Änderungen geschieht durch einen flush(), der durch den commit() der Transaktion implizit ausgelöst wird (transaktional annotierte Business-Methode), oder auch bei der Verwendung einer “managed“ Bean in einer neuen Query einer bereits laufenden Transaktion, oder auch explizit durch den Aufruf von flush() innerhalb einer UserTransaction. Bereits geänderte Beans, die durch einen entsprechenden nicht abgefangenen Fehlerfall nicht mehr gespeichert  werden, können einfach beim nächsten flush() Aufruf gespeichert werden. Ein Standard PersistenceContext kann in eine @Stateless oder @Stateful EJB mittels folgender Annotation injectet werden (Stateful EJB mit Standard PersistenceContext):

@Stateful
public class Service
{
    @PersistenceContext
    private EntityManager em;
}

Dieser EntityManager (PersistenceContext) hat per Default den Scope der Lebensdauer der Transaktion (UserTransaction) die durch Aufruf von tx.begin() und tx.end() begrenzt ist.

Wenn jedoch, wie im folgenden Beispiel, ein PersistenceContext vom Typ PersistenceContextType.EXTENDED verwendet wird, lebt der PersistenceContext solange, wie der Scope des Consumer Objekts ist, in welches er injectet wird und hält solange alle geänderten Objekte (über den EntityManger/die HibernateSession) verfügbar, bis die Transaktion in der er verwendet wird, commitet wird. Bei Verwendung eines PersistenceContext vom Typ PersistenceContextType.EXTENDED wird im Zusammenhang mit Objekten, die über diesen PersistenceContext (EntityManager/HibernateSession) erhältlich sind, nie eine LazyInitializationException auftreten (Stateful EJB mit PersistenceContext vom Typ PersistenceContextType.EXTENDED):

@Stateful
public class Service {
    @PersistenceContext(type = 
          PersistenceContextType.EXTENDED)
    private EntityManager em;
}

@SessionScoped PersistenceContext:

Der @SessionScoped PersistenceContext beginnt mit der Erzeugung der User Session und wird weggeräumt/zerstört wenn die User Session beendet ist. Mehrfache Transaktionen können innerhalb der Lebensdauer dieses Contexts durch Aufruf der EJB Business Methoden ausgeführt werden.

Der PersistenceContext ist also wie ein Transaktions-Monitor und ein Cache der die “managed“ Beans enthält. Wenn auf dem PersistenceContext (EntityManager/Session) Operationen ausgeführt werden, wie z.B. flush() kann die gerade laufende Transaktion commitet werden. Ein PersistenceContext vom Typ PersistenceContextType.EXTENDED ist vergeleichbar einem @Dependent Objekt (wenn man von der Proxy-Eigenschaft absieht). Es ist begrenzt in seiner Lebensdauer durch die Lebensdauer der EJB, in die es injectet wird. Ein Standard PersistenceContext mit Transaction Scope hat genau die Lebensdauer der Transaktion. Somit erhält man bei jedem Business-Methodenaufruf einen neuen PersistenceContext bzw. sogar bei jeder Verwendung einer @Stateless annotierten EJB, so wie im Seam Persistence Framework).

@ConversationScoped PersistenceContext

Fein-granularere Kontrolle über die transaktionalen Prozessabschnitte eines implementierten Use Cases erhält man mit dem folgenden @ConversationScoped PersistenceContext, womit bestimmte Aktionen innerhalb eines Conversational Scopes innerhalb einer kurzen Transaktion laufen können (z.B. “Proceed“-Action), und andere nicht (z. B. “Next Page“-Action). Die Dauer der Transaktion ist dann maximal so lange wie der Aufruf der @Stateful/@Stateless EJB Business Methode. @ConversationScoped PersistenceContexts vom Typ PersistenceContextType.EXTENDED sind also sehr praktisch in Applikationen, da der JPA Entity Cache (EntityManager) die ganze Conversation über die “managed“ Beans enthält, es keine LazyInitializationExceptions gibt und der für den JPA Entity Cache verwendete Arbeitsspeicher am Ende der Conversation gelöscht wird.

Hier eine Stateful EJB mit ConversationScoped PersistenceContext:

@Stateful
@ConversationScoped
public class Service
{
    @PersistenceContext(type = 
        PersistenceContextType.EXTENDED)
    private EntityManager em;
}

Der @ConversationScoped PersistenceContext wird erzeugt durch den Aufruf von conversation.begin() und  wird am Ende des Requests zerstört, wenn conversation.end() aufgerufen wird. Mehrfache Transaktionen können innerhalb der Lebensdauer dieses Contexts durch Aufruf der EJB Business Methoden ausgeführt werden.

Transaktionen beginnen und enden gemäß JTA im selben Request. Um mit Hibernate einen Extended PersistenceContext zu bekommen, kann bei der Verwendung des Seam Persistence Moduls einfach der  Flush Mode der Hibernate Session dahingehend geändert werden, dass das Auto-flushing Feature am Beginn der Conversation ausgeschaltet wird und dann manuell flush() am Ende der Conversation aufgerufen wird. Somit verbrauchen Transaktionen wenig Datenbank-System Ressourcen und die User Transaktionen sind immer noch atomar mit guter Transaktionsisolation, als hätte man eine wirklich über die ganze Conversation laufende Transaktion.

@RequestScoped und Custom Scoped PersistenceContext

Ein PersistenceContext vom Typ PersistenceContextType.EXTENDED kann in eine Bean beliebigen Scopes injected werden das gilt auch für eine @RequestScoped Bean oder Custom Scoped Bean. Der Context wird  erzeugt, wenn der Request beginnt und wird zerstört, wenn der Request endet.

Mehrfache Transaktionen können innerhalb der Lebensdauer jedes Contexts durch Aufruf der @Stateful oder @Stateless EJB Business Methoden ausgeführt werden bei Verwendung eines PersistenceContext vom Typ PersistenceContextType.EXTENDED.

Best Practices sind: @ConversationScoped und @RequestScoped(CustomScoped) PersistenceContext.

Das “Seam Persistence“ Modul – Einsatz über EJB/JTA hinaus

Seam 3 bietet ein sehr angenehmes Persistenz Modul mit einem API für kurzlebige Persistenz Contexte (nicht für PersistenceContexts vom Typ PersistenceContextType.EXTENDED), mit einer sehr guten Unterstützung und CDI-basierten Anbindung an die Seam Rich Faces innerhalb des JBoss Developer Studios. Darüber hinaus kann auch jederzeit Hibernate (von RedHat) innerhalb der robusten Java EE Seam Faces Applikationen verwendet werden oder mit anderen GUI/Javascript-generierenden Frameworks, wie Vaadin, oder mit GWT. Deklarative @Transactional(BEGIN) und @Transactional(END) annotierte Methoden können ebenfalls verwendet werden wie UserTransactions, womit man alle Möglichkeiten hat. Zusätzlich können weitere CDI Erweiterungen verwendet werden und Interceptoren (ähnlich wie bei Spring AOP).

EJBs wrappen erzeugten Exceptions in EJBExceptions

Software Persistenz-Subsysteme mit Hibernate haben oft einen DataAccessObject (DAO) Layer, in welchem Standard Exceptions verwendet werden, um das Ergebnis allgemein üblicher Operationen wiederzugeben. Dabei werden üblicherweise solche Exceptions wie eine NoSuchObjectException, oder eine  DuplicateObjectException ausgelöst bzw. gefangen/ausgewertet. Wenn der Hibernate Layer darauf beruht,  zur Wiederherstellung diese Exceptions zu fangen und auszuwerten und danach weiterhin korrekt zu funktionieren, sind die Entwickler beim Wechsel auf EJBs evtl. überrascht, wenn jede dieser Exceptions fortan in einer EJBException gewrappt wird.

Betrachtet man dies aus der Perspektive des transaktionalen Software System, gilt Folgendes:  EJB verwendet JTA, muss also im Fall einer Exception wissen, ob es den Rollback der Transaktion durchführen soll oder nicht. Um dieses Konzept zu vereinfachen, hat EJB hat das Konzept der  @ApplicationException.

“Application” Exceptions gegenüber “System” Exceptions:

  • Eine Application Exception ist eine Exception, die eine Bedeutung für den Client/die Consumer Bean des Services hat und Auswirkungen auf die Fehlerbehandlung hinsichtlich der  Programmablauflogik, Navigation, oder der weiteren Verwendung von Daten sowie deren Gültigkeit innerhalb der Anwendung.
  • Eine System Exception ist eine Exception, die wegen einem nicht-behebbaren Fehler in den  Services des darunterliegenden Systems ensteht und von der sich das System nicht wieder erholen kann, sie kann auch vom Client/der Consumer Bean nicht weiter behandelt werden (ausser einer sehr grundlegenden Fehlerbehandlung, wie z.B. einem zugeordneten Fehlercode für die Server-Nichtverfügbarkeit, wie z. B. dem HTTP-Statuscode 500 und einer allgemeinen Fehlermeldung)

Als Default, wird jede “Unchecked Exception”(RuntimeException), die von einem EJB Service generiert wird, als “System Exception” behandelt, was bedeutet, dass die Transaktion einen Rollback ausführen sollte, da ein unbehebbarer Fehler aufgetreten ist. Diese System Exception wird dann in ein EJBException Objekt gewrappt, sollte aber nicht behandelt und nicht gefangen werden. Maximal eine sehr grundlegende Fehlerbehandlung kann darauf noch folgen, z. B. die Weiterleitung auf eine generierte Fehlerseite, oder auf die Startseite eines PageFlows (Reset in den Idle Zustand).

Dagegen müssen die Exceptions, die nach der Anforderung nicht den Zustand der aktuell laufenden Transaktion ändern sollen, also keinen Rollback auslösen sollen, der Applikation bekanntgemacht werden. Die entsprechende Exception Klasse muss hierfür entweder mit der @ApplicationException  Annotation annotiert werden, oder, wenn der Sourcecode der Exception nicht geändert werden kann, muss diese Klasse mit ihrem vollqualifizierten Packagenamen in der Konfigurationsdatei /WEB-INF/ejb-jar.xml als <application-exception> Element eingetragen werden. Mit einer gut gewählten Exception-Hierarchie dauert dies nicht zu lange, denn nur die Top-Level Exception muss konfiguriert werden, weil abgeleitete Exception-Klassen automatisch die Konfiguration ihrer Exception-Basisklassen und –Typen erben.

Hier ein paar beispielhafte Exception-Definitionen in einer Beispiel /WEB-INF/ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" version="3.1"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
   
   <assembly-descriptor>
      <application-exception>
         <exception-class>javax.persistence.PersistenceException</exception-class>
         <rollback>true</rollback>
      </application-exception>
      <application-exception>
         <exception-class>com.example.exceptions.DataException</exception-class>
         <rollback>true</rollback>
      </application-exception>
   </assembly-descriptor>
</ejb-jar>

Nun werden die Exceptions entweder als Transaktions-Grenzen behandelt, oder werden ignoriert, abhängig davon, wie das System konfiguriert ist. Dies verhindert Teil-Commits und verhindert zusammen mit dem Conversation-Scope ebenfalls Teil-Commits über Zwischen-Interfaces.

Ich möchte bei dieser Gelegenheit auf das folgende sehr interessante, erfolgreiche und empfehlenswerte Seminar von Binaris Informatik hinweisen:

“TDD mit Java”

Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test Driven Development mit Java, sowie eine schöne Frühlingszeit.

Services – TDD mit Arquillian, REST, JSF2, CDI, JEE6

Die Motivation

Single Page Web-Applikationen (SPAs) haben nicht nur wegen Ihrer guten Laufzeit-Performance in JEE Web-Applikationen zunehmend an Bedeutung gewonnen. Diese können durchaus auch mit JSF 2 Implementierungen, wie dem RichFaces 4 umgesetzt werden und müssen nicht immer responsiv funktionieren. Primefaces Mobile Implementierungen können also verwendet werden, wenn eine mobile Version erforderlich ist, ansonsten ist eine RichFaces 4 Open Source Version mit der ihr eigenen Template-Technologie ebenfalls eine gute Lösung, die für Tablets und größere Displays auch gut funktioniert. Über Test-getriebene Entwicklung von SPAs mit responsivem Frontend (mittles Backbone, jQuery, QUnit, Selenium, etc.) gibt es in diesem Blog bereits Einträge und ich möchte bei dieser Gelegenheit auf das folgende sehr interessante, erfolgreiche und empfehlenswerte Seminar von Binaris Informatik hinweisen:

“TDD mit Java”

In diesem Blog-Eintrag zum Thema „Test-driven Development mit SPAs“ soll ausser den Beispielen zu Java/JEE-implementierten integrativen EJB Backend Service-Tests mittels dem Arquillan-Testframework auch eine simple Grusskarten Applikation implementiert werden, welche mittels JSF2 (RichFaces 4) funktioniert und eine Grusskarten-Mail mit dem Link auf ein Grusskartenbild versendet (Auswahl aus dem ‘Primefaces Gallerie‘  Komponenten-Beispiel). Zur Verbesserung der Usability kommen dabei HTML5 und CSS3 (Transitionen) zum Einsatz, ohne jedoch auf Animationen mit jQuery zurückzugreifen.

TDD der Beispiel Applikation

Wie zusätzliche Selenium-Tests mit dem Selenium FireFox-PlugIn durchgeführt und diese Tests gespeichert werden können, um mit solchen integrativen, automatisierten Tests ein paar Grusskarten zu versenden, ist bereits in diesem Blog-Eintrag hier beschrieben, um auch später jederzeit die korrekte Funktionsweise der Eingabe-Aktionen und Link-Aufrufe der SPA Applikation automatisiert über die erstellte Selenium Test-Suite verifizieren zu können.

Die Technologien:

Die Beispiel-Applikation ‘grusskarten‘ verwendet im Frontend JSF2 (RichFaces 4) und HTML5-/CSS3, sowie die .xhtml-Template Technologie und für das JAX-RS Service-Backend Java EE (Stateless EJB 3.1 und JEE 6 mittels CDI) auf dem ‘JBoss AS 7.4‘ mit der Produktbezeichnung ‘JBoss EAP 6.3‘ und auf dem WildFly 8.1.

Für die Weiterentwicklung, den Build und das Deployment des Projekts ist mindestens Java 6 und Maven 3 erforderlich. Als  Entwicklungsumgebung wurde Eclipse 4.3 (Kepler) in Form des “JBoss Developer Studios 7.1“ mit Java 7 verwendet und die FireFox WebDeveloper IDE/Tools.

Hier die verwendete pom.xml (mit der optionalen Primefaces Dependency):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.binaris.grusskarten</groupId>
    <artifactId>grusskarten</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Binaris Greetingcards</name>
    <description>A starter Java EE 6 webapp project for use on JBoss AS 7 / EAP 6, generated from the jboss-javaee6-webapp archetype</description>

	<url>http://repository.jboss.org/nexus/content/groups/public</url>
    <properties>
        <!-- Explicit declaration of source encoding eliminates the following 
            message: -->
        <!-- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered 
            resources, i.e. build is platform dependent! -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- JBoss dependency versions -->
        <version.org.jboss.as.plugins.maven.plugin>7.2.Final</version.org.jboss.as.plugins.maven.plugin>
        <!-- Define the version of the JBoss BOMs we want to import. The 
            JBoss BOMs specify tested stacks. -->
        <version.org.jboss.bom>1.0.0.Final</version.org.jboss.bom>
        <!-- Alternatively, comment out the above line, and un-comment the 
            line below to use version 1.0.0.Final-redhat-1 which is a release certified 
            to work with JBoss EAP 6. It requires you have access to the JBoss EAP 6 
            maven repository. -->
        <!-- <version.org.jboss.bom>1.0.0.Final-redhat-1</version.org.jboss.bom>> -->

        <!-- other plugin versions -->
        <version.compiler.plugin>2.3.1</version.compiler.plugin>
        <version.surefire.plugin>2.4.3</version.surefire.plugin>
        <version.war.plugin>2.1.1</version.war.plugin>

        <!-- maven-compiler-plugin -->
        <maven.compiler.target>1.6</maven.compiler.target>
        <maven.compiler.source>1.6</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- JBoss distributes a complete set of Java EE 6 APIs including 
                a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or 
                a collection) of artifacts. We use this here so that we always get the correct 
                versions of artifacts. Here we use the jboss-javaee-6.0-with-tools stack 
                (you can read this as the JBoss stack of the Java EE 6 APIs, with some extras 
                tools for your project, such as Arquillian for testing) and the jboss-javaee-6.0-with-hibernate 
                stack you can read this as the JBoss stack of the Java EE 6 APIs, with extras 
                from the Hibernate department of projects) -->
            <dependency>
                <groupId>org.jboss.bom</groupId>
                <artifactId>jboss-javaee-6.0-with-tools</artifactId>
                <version>${version.org.jboss.bom}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.bom</groupId>
                <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>
                <version>${version.org.jboss.bom}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <!-- First declare the APIs we depend on and need for compilation. 
            All of them are provided by JBoss AS 7 -->
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.annotation</groupId>
            <artifactId>jboss-annotations-api_1.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.ws.rs</groupId>
            <artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- Import the JSF API, we use provided scope as the API is included 
             in JBoss AS 7 -->
        <dependency>
            <groupId>org.jboss.spec.javax.faces</groupId>
            <artifactId>jboss-jsf-api_2.1_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Annotation processor to generate the JPA 2.0 metamodel classes 
             for typesafe criteria queries -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Annotation processor to raise compilation errors whenever 
             constraint annotations are incorrectly used. -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator-annotation-processor</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Needed for running tests (you may also use TestNG) -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Arquillian allows you to test enterprise code such as EJBs and 
            Transactional(JTA) JPA from JUnit/TestNG -->
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.protocol</groupId>
            <artifactId>arquillian-protocol-servlet</artifactId>
            <scope>test</scope>
        </dependency>
		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
			<version>1.4.4</version>
		</dependency>
		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>javax.mail-api</artifactId>
			<version>1.5.2</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>4.0</version>
		</dependency>
    </dependencies>

    <build>
        <!-- Maven will append the version to the finalName (which is the 
            name given to the generated war, and hence the context root) -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- Compiler plugin enforces Java 1.6 compatibility and activates 
                annotation processors -->
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${version.compiler.plugin}</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>${version.war.plugin}</version>
                <configuration>
                    <!-- Java EE 6 doesn't require web.xml, Maven needs to know -->
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <!-- To use, run: mvn package jboss-as:deploy -->
            <plugin>
                <groupId>org.jboss.as.plugins</groupId>
                <artifactId>jboss-as-maven-plugin</artifactId>
                <version>${version.org.jboss.as.plugins.maven.plugin}</version>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <!-- The default profile skips all tests, though you can tune 
                 it to run just unit tests based on a custom pattern -->
            <id>default</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>${version.surefire.plugin}</version>
                        <configuration>
                            <skip>true</skip>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <!-- An optional Arquillian testing profile that executes tests 
				the profile will start a new JBoss AS instance, execute 
                the test, shut it down when done -->
            <!-- Run with: mvn clean test -Parq-jbossas-managed -->
            <id>arq-jbossas-managed</id>
            <dependencies>
                <dependency>
                    <groupId>org.jboss.as</groupId>
                    <artifactId>jboss-as-arquillian-container-managed</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <!-- An optional Arquillian testing profile that executes tests 
                in a remote JBoss AS instance -->
            <!-- Run with: mvn clean test -Parq-jbossas-remote -->
            <id>arq-jbossas-remote</id>
            <dependencies>
                <dependency>
                    <groupId>org.jboss.as</groupId>
                    <artifactId>jboss-as-arquillian-container-remote</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
    <repositories>
        <repository>
            <id>jboss</id>
            <name>JBoss Release Repository</name>
            <url>http://repository.jboss.org/maven2</url>
        </repository>
        <repository>
	        <id>jboss-snapshot</id>
	        <name>JBoss Maven Repo</name>
	        <url>http://snapshots.jboss.org/maven2</url>
	    </repository>
    </repositories>
</project>

Über CDI und JavaEE gibt es bereits Informationen in diesem Blog. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF Verzeichnis.

Die Architektur:

a) Frontend:

Das JSF2 Servlet wird über die folgende faces-config.xml aktiviert:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This file is optional for any extra configuration. -->
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
    <!-- This descriptor activates the JSF 2.0 Servlet -->
    <!-- Navigation rules go here. Using CDI for creating @Named annotated managed beans. -->
    <!-- No vavigation rules required for a SPA with only one site -->
</faces-config>

Über CDI und JavaEE gibt es bereits Informationen in diesem Blog. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF Verzeichnis.

Der Aufruf erfolgt über einen Redirect in der index.html:

<!-- Plain HTML page redirecting to index.jsf -->
<html>
<head>
        <meta http-equiv="Refresh" content="0; URL=index.jsf">
</head>
</html>

Das Template für die einzige SPA-Seite befindet sich unter /WEB-INF/templates/template.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <title>Greetingcards</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <h:outputStylesheet name="css/screen.css" />
</h:head>
<h:body>
    <div id="container">
        <div id="content">
            <ui:insert name="content">
            </ui:insert>
        </div>
        <div id="aside">
            <p>Learn more about JBoss Enterprise Application Platform 6.</p>
            <ul>
                <li><a href="https://access.redhat.com/knowledge/docs/JBoss_Enterprise_Application_Platform/">Documentation</a></li>
                <li><a href="http://red.ht/jbeap-6">Product Information</a></li>
            </ul>
            <p>Learn more about JBoss AS 7.</p>
            <ul>
                <li><a
                    href="http://www.jboss.org/jdf/quickstarts/jboss-as-quickstart/guide/Introduction">Getting
                        Started Developing Applications Guide</a></li>
                <li><a href="http://jboss.org/jbossas">Community
                        Project Information</a></li>
            </ul>
            <p>Learn more about TDD with Java <a href="http://binaris-informatik.de/?p=2275">here</a> and <a href="https://www.testdrivendevelopment.de/">here</a></p>
        </div>
        <div id="footer">
            <p>
                This project was generated from a Maven archetype from JBoss.<br />
            </p>
        </div>
    </div>
</h:body>
</html>

Die einzige Seite index.xhtml der SPA wird im div der id “content“ per <ui:insert>integriert:

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    template="/WEB-INF/templates/template.xhtml">
    <ui:define name="content">
        <h1>Welcome to Greetingcards!</h1>

        <div>
            <p>Greetingcards - a Java EE 6 Enterprise Application.</p>
            <h3>Application runs on:</h3>
            <img src="resources/gfx/dualbrand_as7eap.png" />
        </div>

        <h:form id="reg">
            <h2>Recipient and Greetingcard Registration</h2><br />
            <h:panelGrid columns="7" columnClasses="titleCell">
                <h:outputLabel for="name" value="Name:" />
                <h:inputText id="name" value="#{newRecipient.name}" />
                <h:message for="name" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />

                <h:outputLabel for="email" value="Email:" />
                <h:inputText id="email" value="#{newRecipient.email}" />
                <h:message for="email" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />

                <h:outputLabel for="message" value="Message:" />
                <h:inputText id="message"
                    value="#{newRecipient.message}" />
                <h:message for="message" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
                <h:message for="pathToCard" errorClass="invalid" />
            </h:panelGrid>
            <h2>Choose a picture card to send it:</h2>
            <h:panelGrid columns="7">
            	<h:commandButton id="register1" action="#{recipientController.register('resources/gfx/galleria1.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria1.jpg" />
            	<h:commandButton id="register2" action="#{recipientController.register('resources/gfx/galleria2.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria2.jpg" />
            	<h:commandButton id="register3" action="#{recipientController.register('resources/gfx/galleria3.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria3.jpg" />
            	<h:commandButton id="register4" action="#{recipientController.register('resources/gfx/galleria4.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria4.jpg" />
            	<h:commandButton id="register5" action="#{recipientController.register('resources/gfx/galleria5.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria5.jpg" />
            	<h:commandButton id="register6" action="#{recipientController.register('resources/gfx/galleria6.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria6.jpg" />
            	<h:commandButton id="register7" action="#{recipientController.register('resources/gfx/galleria7.jpg')}" value="Register" styleClass="card" image="resources/gfx/galleria7.jpg" />
            </h:panelGrid>
            <h:panelGrid columns="1">
                <h:messages styleClass="messages"
                    errorClass="invalid" infoClass="valid"
                    warnClass="warning" globalOnly="true" />
            </h:panelGrid>
        </h:form>
        <h2>Recipients and Greetingcards sent:</h2>
        <h:panelGroup rendered="#{empty recipients}">
            <em>No registered recipients.</em>
        </h:panelGroup>
        <h:dataTable var="recipient" value="#{recipients}"
            rendered="#{not empty recipients}"
            styleClass="simpletablestyle">
            <h:column>
                <f:facet name="header">Id</f:facet>
                #{recipient.id}
            </h:column>
            <h:column>
                <f:facet name="header">Name</f:facet>
                #{recipient.name}
            </h:column>
            <h:column>
                <f:facet name="header">Email</f:facet>
                #{recipient.email}
            </h:column>
            <h:column>
                <f:facet name="header">Message</f:facet>
                #{recipient.message}
            </h:column>
            <h:column>
                <f:facet name="header">Card</f:facet>
                <h:button image="#{recipient.pathToCard}" styleClass="card" />
            </h:column>
            <h:column>
                <f:facet name="header">REST URL</f:facet>
                <a href="#{request.contextPath}/rest/recipients/#{recipient.id}">/rest/recipients/#{recipient.id}</a>
            </h:column>
            <f:facet name="footer">
            REST URL for all recipients: <a
                    href="#{request.contextPath}/rest/recipients">/rest/recipients</a>
            </f:facet>
        </h:dataTable>
    </ui:define>
</ui:composition>

Die register(…) Action des RecipientController wird durch einen Click auf ein Grusskarten Bild in der Gallerie aufgerufen, z. B.:  action=“#{recipientController.register(‚resources/gfx/galleria7.jpg‘)}“

Hier der RecipientController mit der auszuführenden Action:

package de.binaris.grusskarten.controller;

import java.util.HashMap;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import de.binaris.grusskarten.model.Recipient;
import de.binaris.grusskarten.service.RecipientRegistration;

@Model
public class RecipientController {

    @Inject
    private FacesContext facesContext;

    @Inject
    private RecipientRegistration recipientRegistration;

    @Produces
    @Named
    private Recipient newRecipient;

    @PostConstruct
    public void initNewRecipient() {
    	newRecipient = new Recipient();
    }
    
    public void register(String pathToCard) throws Exception {
        try {
            Logger log = Logger.getLogger("RecipientController");
            HashMap<String, String> cardsRecipient = new HashMap<String, String>();
            newRecipient.setPathToCard(pathToCard);
            cardsRecipient.put("mailRecipientName", newRecipient.getName());
            cardsRecipient.put("mailRecipientEmail", newRecipient.getEmail());
            cardsRecipient.put("message", newRecipient.getMessage());
            cardsRecipient.put("pathToCard", newRecipient.getPathToCard());
            recipientRegistration.setCardsRecipient(cardsRecipient);

            recipientRegistration.register(newRecipient);
        	
            log.info("register: cardsRecipient.get('"+"mailRecipientName"+"'): "+cardsRecipient.get("mailRecipientName"));
            log.info("register: cardsRecipient.get('"+"mailRecipientEmail"+"'): "+cardsRecipient.get("mailRecipientEmail"));
            log.info("register: cardsRecipient.get('"+"message"+"'): "+cardsRecipient.get("message"));
            log.info("register: cardsRecipient.get('"+"pathToCard"+"'): "+cardsRecipient.get("pathToCard"));
        	
            initNewRecipient();
        	
            FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO, "Recipient registered, card sent!", "Registration successful");
            facesContext.addMessage(null, m);
        } catch (Exception e) {
            String errorMessage = getRootErrorMessage(e);
            FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR, errorMessage, "Registration unsuccessful");
            facesContext.addMessage(null, m);
        }
    }

    private String getRootErrorMessage(Exception e) {
        // error message when registration fails.
        String errorMessage = "Registration failed. See server log for more information";
        if (e == null) {
            // default error messages
            return errorMessage;
        }
        // find the root cause
        Throwable t = e;
        while (t != null) {
            // Get the message from the Throwable class instance
            errorMessage = t.getLocalizedMessage();
            t = t.getCause();
        }
        // root cause message
        return errorMessage;
    }
}

b) Service Backend:

Dabei entspricht die im Service-Backend verwendeten Stateless Session Beans (EJB 3.1) vom Design Pattern her der Facade (SLSB – Stateless Session Bean) für ein einheitliches Interface. Die SLSBs wird hier ohne deklarativ annotierte @Transaction oder zusätzliche User Transaction verwendet. Hier das RecipientRepository der für die Grusskarten verwendeten Empfänger unter Verwendung der JPA Recipient Entity und der benötigten Queries:

package de.binaris.grusskarten.data;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import java.util.List;

import de.binaris.grusskarten.model.Recipient;

@ApplicationScoped
public class RecipientRepository {

    @Inject
    private EntityManager em;

    public Recipient findById(Long id) {
        return em.find(Recipient.class, id);
    }

    public Recipient findByEmail(String email) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Recipient> criteria = cb.createQuery(Recipient.class);
        Root<Recipient> recipient = criteria.from(Recipient.class);
        // Swap criteria statements to try out type-safe criteria queries, a new
        // feature in JPA 2.0
        // criteria.select(member).where(cb.equal(member.get(Recipient_.name), email));
        criteria.select(recipient).where(cb.equal(recipient.get("email"), email));
        return em.createQuery(criteria).getSingleResult();
    }

    public List<Recipient> findAllOrderedByName() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Recipient> criteria = cb.createQuery(Recipient.class);
        Root<Recipient> recipient = criteria.from(Recipient.class);
        // Swap criteria statements to try out type-safe criteria queries, a new
        // feature in JPA 2.0
        // criteria.select(recipient).orderBy(cb.asc(member.get(Recipient_.name)));
        criteria.select(recipient).orderBy(cb.asc(recipient.get("name")));
        return em.createQuery(criteria).getResultList();
    }
}

Für die Verwendung von JPA 2.0 im obigen RecipientRepository generiert Maven mitels pom.xml im Verzeichnis target/generated-sources/de/binaris/grusskarten/model die abstrakte Klasse Recipient_java, die bei Bedarf dem Build Path hinzuzufügen ist, mit den folgenden Metadaten:

package de.binaris.grusskarten.model;

import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Recipient.class)
public abstract class Recipient_ {

	public static volatile SingularAttribute<Recipient, String> message;
	public static volatile SingularAttribute<Recipient, Long> id;
	public static volatile SingularAttribute<Recipient, String> email;
	public static volatile SingularAttribute<Recipient, String> name;
	public static volatile SingularAttribute<Recipient, String> pathToCard;

}

Hier nun der RecipientRegistration-Service (als SLSB):

package de.binaris.grusskarten.service;

import java.util.HashMap;
import java.util.logging.Logger;

import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.persistence.EntityManager;

import de.binaris.grusskarten.model.Recipient;
import de.binaris.grusskarten.util.MailSender;

// @Stateless annotation without explicit manual transaction demarcation
@Stateless
public class RecipientRegistration {

    @Inject
    private Logger recipientLogger;

    @Inject
    private EntityManager em;

    @Inject
    private Event<Recipient> recipientEventSrc;

    private HashMap<String, String> cardsRecipient;
    
    public void register(Recipient recipient) throws Exception {
    	recipientLogger.info("Registering " + recipient.getName());
        em.persist(recipient);
        recipientEventSrc.fire(recipient);
        
        recipientLogger.info("Registered. ");
        try {
	MailSender.sendMessage("mycards@yahoo.com", 
	"password", 
	cardsRecipient.get("mailRecipientName"),
	cardsRecipient.get("mailRecipientEmail"), 
	cardsRecipient.get("message"), 
	cardsRecipient.get("pathToCard"));
        } catch (AddressException e) {
	e.printStackTrace();
        } catch (MessagingException e) {
	e.printStackTrace();
        }
    }
    
    public void setCardsRecipient(HashMap<String, String> cardsRecipient) {
    	this.cardsRecipient = cardsRecipient;
    }
    
    public HashMap<String, String> getCardsRecipient() {
    	return cardsRecipient;
    }
}

c) Der REST Webservice:

Über den Einsatz von RESTful Webservices für SPAs unter Verwendung von JBoss RESTEasy gibt es bereits Blog-Einträge in diesem Blog. Die Aktivierung des RESTEasy-Webservices geschieht dort (Servlet 2.3, 2,4 oder 2.5) über die web.xml:

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
   <display-name>Archetype Created Web Application</display-name>
   <context-param>
      <param-name>javax.ws.rs.core.Application</param-name>
      <param-value>de.binaris.rest.samples.service.SumOfMultiplesApplication</param-value>
   </context-param>

   <context-param>
      <param-name>resteasy.servlet.mapping.prefix</param-name>
      <param-value>/resteasy</param-value>
   </context-param>

   <listener>
      <listener-class>
         org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
      </listener-class>
   </listener>

   <servlet>
      <servlet-name>Resteasy</servlet-name>
      <servlet-class>
         org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
      </servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>Resteasy</servlet-name>
      <url-pattern>/resteasy/*</url-pattern>
   </servlet-mapping>
</web-app>

Stattdessen wird nun die folgende Klasse JaxRsActivator verwendet, die von Application abgeleitet ist und mit der @ApplicationPath Annotation annotiert ist, da seit servlet-3.0 keine web.xml mehr benötigt wird, sondern optional alles über Annotationen deklarierbar ist (Hinweis: wenn beides verwendet wird .xml-Deklaration und Annotation, überschreibt die .xml-Deklaration die Funktionalität der Annotation):

package de.binaris.grusskarten.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
    /* neither extended class attributes nor methods required */
}

Hier der Beispiel REST-Webservice zum Eintragen des Recipients und dem Versand der Card:

package de.binaris.grusskarten.rest;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import de.binaris.grusskarten.data.RecipientRepository;
import de.binaris.grusskarten.model.Recipient;
import de.binaris.grusskarten.service.RecipientRegistration;

/**
 * JAX-RS Example
 * <p/>
 * This class produces a RESTful service to read/write the contents of the recipients table.
 */
@Path("/recipients")
@RequestScoped
public class RecipientResourceRESTService {
    
    @Inject
    private Logger recipientLogger;

    @Inject
    private Validator validator;

    @Inject
    private RecipientRepository repository;

    @Inject
    RecipientRegistration registration;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Recipient> listAllRecipients() {
        return repository.findAllOrderedByName();
    }

    @GET
    @Path("/{id:[0-9][0-9]*}")
    @Produces(MediaType.APPLICATION_JSON)
    public Recipient lookupRecipientById(@PathParam("id") long id) {
        Recipient recipient = repository.findById(id);
        if (recipient == null) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }
        return recipient;
    }

    /**
     * Creates a new member from the values provided. Performs validation, and will return a JAX-RS response with either 200 ok,
     * or with a map of fields, and related errors.
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response createRecipient(Recipient recipient) {

        Response.ResponseBuilder builder = null;
        try {
            // Validates member using bean validation
            validateRecipient(recipient);
            registration.register(recipient);

            // Create an "ok" response
            builder = Response.ok();
        } catch (ConstraintViolationException ce) {
            // Handle bean validation issues
            builder = createViolationResponse(ce.getConstraintViolations());
        } catch (ValidationException e) {
            // Handle the unique constrain violation
            Map<String, String> responseObj = new HashMap<String, String>();
            responseObj.put("email", "Email taken");
            builder = Response.status(Response.Status.CONFLICT).entity(responseObj);
        } catch (Exception e) {
            // Handle generic exceptions
            Map<String, String> responseObj = new HashMap<String, String>();
            responseObj.put("error", e.getMessage());
            builder = Response.status(Response.Status.BAD_REQUEST).entity(responseObj);
        }
        return builder.build();
    }

    /**
     * <p>
     * Validates the given Recipient variable and throws validation exceptions based on the type of error. If the error is standard
     * bean validation errors then it will throw a ConstraintValidationException with the set of the constraints violated.
     * </p>
     * <p>
     * If the error is caused because an existing recipient with the same email is registered it throws a regular validation
     * exception so that it can be interpreted separately.
     * </p>
     * 
     * @param recipient Recipient to be validated
     * @throws ConstraintViolationException If Bean Validation errors exist
     * @throws ValidationException If member with the same email already exists
     */
    private void validateRecipient(Recipient recipient) throws ConstraintViolationException, ValidationException {
        // Create a bean validator and check for issues.
        Set<ConstraintViolation<Recipient>> violations = validator.validate(recipient);

        if (!violations.isEmpty()) {
            throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
        }
        // Check the uniqueness of the email address
        if (emailAlreadyExists(recipient.getEmail())) {
//            throw new ValidationException("Unique Email Violation");
        }
    }

    /**
     * Creates a JAX-RS "Bad Request" response including a map of all violation fields, and their message. This can then be used
     * by clients to show violations.
     * 
     * @param violations A set of violations that needs to be reported
     * @return JAX-RS response containing all violations
     */
    private Response.ResponseBuilder createViolationResponse(Set<ConstraintViolation<?>> violations) {
    	recipientLogger.fine("Validation completed. violations found: " + violations.size());
        Map<String, String> responseObj = new HashMap<String, String>();

        for (ConstraintViolation<?> violation : violations) {
            responseObj.put(violation.getPropertyPath().toString(), violation.getMessage());
        }
        return Response.status(Response.Status.BAD_REQUEST).entity(responseObj);
    }

    /**
     * Checks if a recipient with the same email address is already registered. 
     * This is the only way to easily capture the
     * "@UniqueConstraint(columnNames = "email")" constraint from the Recipient class.
     * 
     * @param email The email to check
     * @return True if the email already exists, and false otherwise
     */
    public boolean emailAlreadyExists(String email) {
        Recipient recipient = null;
        try {
        	recipient = repository.findByEmail(email);
        } catch (NoResultException e) {
            // ignore
        }
        return recipient != null;
    }
}

Die Yahoo Mail Account-Session, um die Grusskarten Mail über den Account userMail zu versenden, erhält man folgendermaßen:

/**
 * get a Yahoo mail account session to send the email via account userMail
 * 
 * @param userMail
 * @return Session
 */
public static Session getMailSenderSessionYahoo(String userMail) {
	
    final Properties props = System.getProperties();
    props.setProperty( "mail.smtp.from", userMail);
    props.setProperty( "mail.smtp.host", "smtp.mail.yahoo.com" );
    props.setProperty( "mail.smtp.auth", "true" );
    props.setProperty( "mail.smtp.port", "465" );
    props.setProperty( "mail.smtp.starttls.enable", "true");
    props.setProperty( "mail.smtp.quitwait", "false");
    props.setProperty( "mail.smtp.ssl.enabled", "true");
    props.setProperty( "mail.smtp.socketFactory.port", "465" );
    props.setProperty( "mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory" );
    return Session.getInstance( props, null);
}

d) Die Datenbank:

Die Testdaten aus der import.sql:

insert into Recipient (id, name, email, message, pathToCard) values (0, 'Banana Joe', 'banana.joe@bananajoe.com', 'Hey Joe, wazzuuup... call me.', 'resources/gfx/galleria1.jpg')

Die persistence.xml aus dem /META-INF Unterverzeichnis zur Aktivierung von JPA 2 sieht für die Verwendung der HSQL Beispiel-Datenbank folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
   xmlns="http://java.sun.com/xml/ns/persistence" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="grusskarten">
      <jta-data-source>java:jboss/datasources/GrusskartenDS</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop" />
         <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

Die Verwendung von JPA 2 ist am folgenden <persistence …>-Element sehr gut zu erkennen. Denn ohne dass hier die version=“2.0“ eingetragen ist, fährt man logischerweise stets nur persistence 1.0, d.h. z.B. JPA 1.

<persistence version="2.0"
   xmlns="http://java.sun.com/xml/ns/persistence" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 

Für eine produktive Applikation und Datenbank sollte die Datasource in der standalone.xml entsprechend konfiguriert werden.

Für den Blog-Eintrag der Beispiel-Applikation soll die Beschreibung der Verwendung der HSQL “in memory“-Datenbank genügen. Auf dem Testserver ist dann eine MySQL Server Datenbank installiert und die entsprechende Datasource konfiguriert. Hier der Link zur Beispiel-Applikation:

http://educationorganizer.de:8485/grusskarten/index.html

Hierfür wird im /WEB-INF-Verzeichnis neben der beans.xml (CDI-Aktivierung) auch die folgende grusskarten-ds.xml hinterlegt:

<?xml version="1.0" encoding="UTF-8"?>
<!-- unmanaged datasource for testing purposes only uses H2, 
        an in memory database that ships with JBoss AS. -->
<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema   
    http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">

    <!-- The datasource is bound into JNDI at this location. We reference 
        this in META-INF/persistence.xml -->
    <datasource jndi-name="java:jboss/datasources/GrusskartenDS"
        pool-name="grusskarten" enabled="true"
        use-java-context="true">
        <connection-url>
jdbc:h2:mem:grusskarten;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1
        </connection-url>
        <driver>h2</driver>
        <security>
            <user-name>admin</user-name>
            <password>banana_joe</password>
        </security>
    </datasource>
</datasources>

Abschließend noch die Die CSS3-Styles in der screen.css mit den Transitionen in der Klasse .card und dem Style für das .hover-Event über einer Grusskarte .card:hover

screen.css:

/* Core styles for the page */
body {
  margin: 0;
  padding: 0;
  background-color: #4a5d75;
  font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
  font-size: 0.8em;
  color:#363636;
}

#container {
  margin: 0 auto;
  padding: 0 20px 10px 20px;
  border-top: 5px solid #000000;
  border-left: 5px solid #8c8f91;
  border-right: 5px solid #8c8f91;
  border-bottom: 25px solid #4a5d75;
  width: 865px; /* subtract 40px from banner width for padding */
  background: #d5d5d5;
  background-repeat: repeat-x;
  padding-top: 30px;
  box-shadow: 3px 3px 15px #d5d5d5;
}
#content {
  float: left;
  width:500px;
  margin: 20px;
}
#aside {
  font-size: 0.9em;
  width: 275px;
  float: left;
  margin: 20px 0px;
  border: 1px solid #D5D5D5;
  background: #F1F1F1;
  background-image: url(#{request.contextPath}/resources/gfx/asidebkg.png);
  background-repeat: repeat-x;
  padding: 20px;
}
.card {
  font-size: 0.9em;
  transition: all 0.3s ease-in-out 0.5s;
  width: 25px;
  height: 20px;
  float: left;
  margin: 0px 0px;
  border: 1px solid #D5D5D5;
  background: #F1F1F1;
  background-repeat: repeat-x;
  padding: 2px;
}

#aside ul {
  padding-left: 30px;
}
.dualbrand {
  float: right;
  padding-right: 10px;
}
#footer {
  clear: both;
  text-align: center;
  color: #666666;
  font-size: 0.85em;
}
code {
  font-size: 1.1em;
}
a {
  color: #4a5d75;
  text-decoration: none;
}
a:hover {
  color: #369;
  text-decoration: underline;
}
.card:hover {
  transition: all 0.5s ease-in-out;
  width: 130px;
  height: 100px;
}
h1 {
  color:#243446;
  font-size: 2.25em;
}
h2 {
  font-size: 1em;
}
h3 {
  color:#243446;
}
h4 {
}
h5 {
}
h6 {
}
/* Recipient registration styles */
span.invalid {
  padding-left: 3px;
  color: red;
}
form {
  padding: 1em;
  font: 80%/1 sans-serif;
  width: 375px;
  border: 1px solid #D5D5D5;
  background: #F1F1F1;
}
label {
  float: left;
  width: 15%;
  margin-left: 20px;
  margin-right: 0.5em;
  padding-top: 0.2em;
  text-align: right;
  font-weight: bold;
  color:#363636;
}
input {
  margin-bottom: 8px;
}
.register {
  float: left;
  margin-left: 85px;
}

.simpletablestyle {
  background-color:#E6E7E8;
  clear:both;
  width: 550px;
}

.simpletablestyle img {
  border:0px;
}

.simpletablestyle td {
  height:2em;
  padding-left: 6px;
  font-size:11px;
  padding:5px 5px;
}

.simpletablestyle th {
  background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left;
  font-size:12px;
  font-weight:normal;
  padding:0 10px 0 5px;
  border-bottom:#999999 dotted 1px;
}

.simpletablestyle thead {
  background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left;
  height:31px;
  font-size:10px;
  font-weight:bold;
  color:#FFFFFF;
  text-align:left;
}

.simpletablestyle .header a {
  color:#94aebd;
}

.simpletablestype tfoot {
  height: 20px;
  font-size: 10px;
  font-weight: bold;
  background-color: #EAECEE;
  text-align: center;
}

.simpletablestyle tr.header td {
  padding: 0px 10px 0px 5px;
}

.simpletablestyle .subheader {
  background-color: #e6e7e8;
  font-size:10px;
  font-weight:bold;
  color:#000000;
  text-align:left;
}

/* Using new CSS3 selectors for styling*/
.simpletablestyle tr:nth-child(odd) {
  background: #f4f3f3;
}
.simpletablestyle tr:nth-child(even) {
  background: #ffffff;
}

.simpletablestyle td a:hover {
  color:#3883ce;
  text-decoration:none; 
}

Die Test-Frameworks und die Tests

a) Die Arquillian Tests für das Service-Backend werden folgendermaßen aktiviert:

Das /test-Verzeichnis neben dem /WEB-INF-Verzeichnis des grusskarten.war hat hierfür folgende Struktur:

- src/test/
    - resources
        - META-INF   
            test-persistence.xml
        - arquillian.xml
        - grusskarten-test-ds.xml
    - java
        - de.binaris.grusskarten.test
            RecipientRegistrationTest.java

Mit der folgenden test-persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
   xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="grusskarten">
      <!-- We use a different datasource for tests, so as to not overwrite 
         production data. This is an unmanaged data source, backed by H2, an in memory 
         database. Production applications should use a managed datasource. -->
      <!-- The datasource is deployed as WEB-INF/test-ds.xml, 
         you can find it in the source at src/test/resources/grusskarten-test-ds.xml -->
      <jta-data-source>java:jboss/datasources/GrusskartenTestDS</jta-data-source>
      <properties>
         <!-- Properties for Hibernate -->
         <property name="hibernate.hbm2ddl.auto" value="create-drop" />
         <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

Und der folgenden arquillian.xml:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
   <!-- Force the use of the Servlet 3.0 protocol with all containers, as it is the most mature -->
   <defaultProtocol type="Servlet 3.0" />
   <!-- Example configuration for a remote JBoss AS 7 instance -->
   <container qualifier="jboss" default="true">
      <!-- If you want to use the JBOSS_HOME environment variable, just delete the jbossHome property -->
      <configuration>
         <property name="jbossHome">/path/to/jboss/as</property>
      </configuration>
   </container>
</arquillian>

Hier die grusskarten-test-ds.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This is an unmanaged datasource. It should be used for proofs of concept 
   or testing only. It uses H2, an in memory database that ships with JBoss 
   AS. -->
<datasources xmlns="http://www.jboss.org/ironjacamar/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
   <!-- The datasource is bound into JNDI at this location. We reference 
      this in META-INF/test-persistence.xml -->
   <datasource jndi-name="java:jboss/datasources/GrusskartenTestDS"
      pool-name="grusskarten-test" enabled="true"
      use-java-context="true">
      <connection-url>jdbc:h2:mem:grusskarten-test;DB_CLOSE_DELAY=-1</connection-url>
      <driver>h2</driver>
      <security>
         <user-name>admin</user-name>
         <password>banana_joe</password>
      </security>
   </datasource>
</datasources>

Und hier die Test-Klasse RecipientRegistrationTest für den Backend Service-Test:

package de.binaris.grusskarten.test;

import static org.junit.Assert.assertNotNull;
import java.util.logging.Logger;
import javax.inject.Inject;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import de.binaris.grusskarten.model.Recipient;
import de.binaris.grusskarten.service.RecipientRegistration;
import de.binaris.grusskarten.util.Resources;

@RunWith(Arquillian.class)
public class RecipientRegistrationTest {
    @Deployment
    public static Archive<?> createTestArchive() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(Recipient.class, RecipientRegistration.class, Resources.class)
                .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                // deploy the test datasource
                .addAsWebInfResource("grusskarten-test-ds.xml");
    }

    @Inject
    RecipientRegistration recipientRegistration;

    @Inject
    Logger recipientLogger;

    @Test
    public void testRegister() throws Exception {
        Recipient newRecipient = new Recipient();
        newRecipient.setName("Django Doe");
        newRecipient.setEmail("django@mailinator.com");
        newRecipient.setMessage("Hey Django, please call me.");
        newRecipient.setPathToCard("resources/gfx/galleria1.jpg");
        recipientRegistration.register(newRecipient);
        assertNotNull(newRecipient.getId());
        recipientLogger.info(newRecipient.getName() + " was persisted with id " + newRecipient.getId());
    }
}

In der pom.xml können unter den Profilen im Default Profil für den skip-Test Wert ‘false‘ eingetragen werden und z. B. der Maven Task  mvn clean test -Parq -jbossas-managed  aufgerufenwerden, damit die Tests ausgeführt werden. Hier noch mal der relevante Abschnitt in der pom.xml:

   <profiles>
        <profile>
            <!-- The default profile skips all tests, though you can tune 
                 it to run just unit tests based on a custom pattern -->
            <id>default</id>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>${version.surefire.plugin}</version>
                        <configuration>
                            <skip>true</skip>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <!-- An optional Arquillian testing profile that executes tests 
				the profile will start a new JBoss AS instance, execute 
                the test, shut it down when done -->
            <!-- Run with: mvn clean test -Parq-jbossas-managed -->
            <id>arq-jbossas-managed</id>
            <dependencies>
                <dependency>
                    <groupId>org.jboss.as</groupId>
                    <artifactId>jboss-as-arquillian-container-managed</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <!-- An optional Arquillian testing profile that executes tests 
                in a remote JBoss AS instance -->
            <!-- Run with: mvn clean test -Parq-jbossas-remote -->
            <id>arq-jbossas-remote</id>
            <dependencies>
                <dependency>
                    <groupId>org.jboss.as</groupId>
                    <artifactId>jboss-as-arquillian-container-remote</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

b) Die Selenium Tests:

Installation: In der Test-Browser Instanz die folgende Installations-Url aufrufen und der Installation zustimmen:

http://release.seleniumhq.org/selenium-ide/2.9.0/selenium-ide-2.9.0.xpi

Selenium-Blog:

http://blog.reallysimplethoughts.com/2015/03/09/selenium-ide-scheduler-has-arrived-part-1/

Selenium Download:

http://docs.seleniumhq.org/download/

Selenium Documentation:

http://docs.seleniumhq.org/docs/
https://seleniumhq.github.io/docs/
http://seleniumhq.github.io/selenium/docs/api/java/index.html

Die Tests werden folgendermaßen angelegt, nachdem das Selenium IDE PlugIn installiert wurde. Dabei sollten folgende Grundregeln beachtet werden:

– Tests sollten immer eine definierte Start-Url haben, z.B. hier: http://localhost:8080/grusskarten/

– Tests sollten nicht von anderen Tests abhängig sein.

– Tests sollten immer nur eine Sache/einen kleinen Workflow je Test Case testen,

z.B. der Versand einer Card.

– Test Cases können in einer Test Suite zusammengefasst werden, um den Automatisierungsgrad zu erhöhen.

Bei dieser Gelegenheit am Ende des Blog-Eintrags darf auch erneut auf das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java” von Binaris Informatik

hingewiesen werden.

Wie Test-Suites, die mit dem Selenium IDE PlugIn durchgeführt wurden und automatisiert ausgeführt werden können, erstellt werden, wurde bereits in einem Blog Eintrag diese Blogs beschrieben.

Hier nun auch das komplette Web-Archiv grusskarten.war zum Download: http://www.4shared.com/file/YxqB4JjRba/grusskarten.html

Hier der JBoss EAP 6.3 bzw. WildFly 8.1 (JBoss AS 7.4) als Jar-Installer zum Download.

http://www.jboss.org/download-manager/file/jboss-eap-6.3.0.GA-installer.jar

Hier noch eine weitere SPA (Single Page Appliction) Beispiel-Applikation, bei der das Testen des Backends mittels Arquillian und das integrative Testen des Frontends mittels Selenium (Workflow und UX Elemente) und QUnit  (Javascript-Framework zum Test-Aufruf der REST-Endpoints aus einer Browser-View) ebenfalls ausführlich beschrieben ist.

Installation/Start der Beispiel-Applikation:

– Das JBoss Zip-Archiv entpacken

– JAVA_HOME, JBOSS_HOME entsprechend setzen und

in der standalone.bat oder standalone.sh verwenden.

– grusskarten.war ins Server-Unterverzeichnis /standalone/deployments speichern

– Server starten per standalone.bat oder standalone.sh, http://localhost:8080/grusskarten/ aufrufen

– Die Selenium Tests erstellen per Selenium IDE PlugIn/Recorder erstellen und ausführen.

Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test Driven Development mit Java, sowie ein angenehmes und fröhliches “Super Bowl“ Wochenende und eine verantwortungsvolle Karnevals-Session.

WebApps – mit Primefaces 6, JSF 2, JPA 2, EJB 3, JEE7, REST auf WildFly 9, 10 und 11

Die Motivation

Über Beispiel-Applikationen zu der der sehr vorteilhaften Primefaces 6-Technologie gibt es bereits Blog-Einträge in diesem Blog hier. In diesem Blog-Eintrag soll nun nach dem zugrundeliegenden relationalen Datenmodell eine Primefaces 6 Beispiel-Webapplikation shoppinghelper-primefaces für eine MySQL 5 Datenbank Datenbank erstellt werden. Dabei wird als Basis-Frontendtechnologie JSF 2 in der Primefaces 6-Implementierung auf dem WildFly 9, 10 und 11 verwendet. Weiterhin wird der Einsatz moderner Tag-Libs in den XHTML-Seiten der JEE7-Applikation erklärt. Mittels JBoss Forge Eclipse PlugIn wird die Primefaces 6 Beispiel-Applikation shoppinghelper-primefaces auf dem Datenmodell mit den JPA 2 Entities erstellt unter Einsatz von JSF 2 und der robusten, elegant gestylten, etablierten, optimalen Primefaces Frontend-Technologie.

Die Beispiel-Applikation shoppinghelper-primefaces wird dann auf den JBoss WildFly 9 Final, JBoss WildFly 10 Final und JBoss WildFly 11 Application Servern deployt und getestet. Die Verwendung der JBoss WildFly JEE7-Application Server wurde in einem Blog-Eintrag in diesem Blog bereits einmal beschrieben. Dabei liefern die JEE6- und JEE7-Beispielapplikationen hier mit Hilfe eines EJB 3-getriebenen Backends (Session Beans als DAOs, DAO-Pattern) unter Anbindung der JPA 2 Entities (Table-Per-Class – Pattern) mit Hilfe von DTOs über fachliche RESTful ServiceInterfaces die benötigten Daten zur Präsentation und Bearbeitung (Änderung, Löschen, Neuanlage) in den Web-Frontends, welche die WebServices konsumieren (ServiceConsumer-Pattern). Der JPA-Provider ist hier Hibernate von JBoss, wofür WildFly 9, 10 und 11 zu konfigurieren sind. Mittels Criteria-API werden aus dem Backend die benötigten Daten für das Frontend gelesen, welche in der jeweiligen Einkaufsliste (“Shoppinghelper“) dann auswählbar sind.

Erstellung der Beispiel Applikation nach Datenmodell

Wie die Applikation shoppinghelper-primefaces mittels JBoss Forge erstellt werden kann, ist bereits analog in diesem Blog-Eintrag hier für die Applikation surveyapplication-primefaces beschrieben worden und wie daraus die Shoppinghelper-Applikation weiterentwickelt werden kann, wurde bereits im vorigen Blog-Eintrag zur shoppinghelper-angularjs Applikation hier erklärt. Ebenso das mittels DBeaver erstellte Entity-Relationship Diagramm der shoppinghelper-angularjs – MySQL Datenbank hier, welches ebenfalls für die shoppinghelper-primefaces Beispiel-Applikation Verwendung findet, da beide Applikationen auf derselben Datenbank arbeiten. In der pom.xml des jeweiligen Beispielprojekts nach erfolgtem Download erkennbar, wurden in der Applikation shoppinghelper-primefaces ebenfalls die folgenden JEE 7 Artefakte für den JBoss WildFly identifiziert, verwendet und importiert:

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

Es werden in der pom.xml die folgenden Maven Dependencies verwendet:

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Einsatz von CDI

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Über CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog. Die Aktivierung von CDI erfolgt mittels beans.xml im WEB-INF Verzeichnis.

Auch über die Architektur und die verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in einem Blog-Eintrag hier und bereits vorausgegangenen Blog-Einträgen.

Primefaces 6:

Hat man erst einmal die JPA 2.2 Entities definiert und mittels JBoss Forge die Beispiel-Webapplikationen für die ausgewählte Frontend-Technologie (Angular JS oder Faces) erzeugt und rebrandet, ist die Migration der Faces Beispiel-Applikation leicht durchführbar und wurde bereits in den vorigen Blog-Einträgen beschrieben.

Die Einbindung der erforderlichen Primefaces-UI-Bibliothek primefaces-6.0.jar erfolgt dabei ganz simpel übers Hinzufügen der entsprechenden Maven Primefaces Dependency in der pom.xml.

<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.0</version>
</dependency>

Allein durch Änderung dieses Eintrags in der pom.xml kann jederzeit auch auf die gewünschte Primefaces-Version umgestellt werden. Bei der Ausprogrammierung der XHTML-Views auf Primefaces sind der Primefaces Showcase und der neue Primefaces 6.0 Users Guide (PDF) sehr hilfreich. Der größte Aufwand der Migration besteht hierbei im Austausch der Standard MyFaces-Tags (h: Präfix) durch die Primefaces-Tags (p: Präfix) ist also für die simplen Beispiel-Applikationen leicht umsetzbar. Ebenfalls wurde das sehr gute bei DZone erhältliche PDF zur JSTL (Java Server Pages Template Library) verwendet.

Aktiviert werden die Primefaces-Tags in der gewünschten XHTML-Seite mittels folgendem Namespace Eintrag im Dokumenten-Kopf der XHTML-View xmlns:p=“http://primefaces.org/ui und die zusätzliche Funktionalität der JSTL-Tags wird entsprechend, wie in den MyFaces-Views auch, per xmlns:c=“http://java.sun.com/jsp/jstl/core verfügbar gemacht.

Durch die Verwendung von CDI 1.1 haben die JSF ManagedBeans stattdessen die Annotation @Named und bleiben ansonsten nahezu identisch zu den ManagedBeans der MyFaces-Applikationen. Weitere Informationen zu JSF 2.2, CDI und Primefaces finden sich auch hier im Blog von Balus C. Detail-Informationen zur JavaScript-Bibliothek jQuery, die vom Primefaces-Framework sehr intensiv verwendet wird, finden sich hier: jquery.org

forge.taglib.xml:

Die in den Beispiel-Applikationen verwendete, moderne Forge TagLib kann sehr einfach erweitert werden, indem die zusätzlich benötigten Methoden in der ViewUtils-Klasse ergänzt werden und deren Signaturen in der forge.taglib.xml im Verzeichnis src/main/webapp/WEB-INF/classes/META-INF/ bekanntgemacht werden, also genau wie bei klassischen TagLibs mit dem Unterschied, dass die verwendeten Tags innerhalb der Applikation in der ViewUtils-Klasse implementiert werden, was für die Applikation zusätzliche Sicherheit bringt. Diese Tags sind für jede Applikation sehr leicht adaptierbar und können genau auf die Applikation zugeschnitten werden. Die TagLib wird in der entsprechenden .xhtml-View über den Eintrag xmlns:forgeview=“http://jboss.org/forge/view“ mit dem Prefix forgeview unter der DOCTYPE-Definition im ui:composition eingebunden und mittels forgeview-Prefix per Expression Language (EL) an der gewünschten Stelle mit den richtigen Input-Parametern aufgerufen. Hier die forge.taglib.xml für die Beispiel-Applikation shoppinghelper-primefaces:

<?xml version=“1.0“ encoding=“UTF-8“?>
<!DOCTYPE facelet-taglib PUBLIC “-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN“ “http://java.sun.com/dtd/facelet-taglib_1_0.dtd“>
<facelet-taglib xmlns=“http://java.sun.com/JSF/Facelet“>
<namespace>http://jboss.org/forge/view</namespace>
<function>
<function-name>asList</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.util.List asList(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>display</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.lang.String display(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>count</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
int count(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>displayShort</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayShort(java.lang.Object, int)
</function-signature>
</function>

<function>
<function-name>displayRange</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayRange(java.lang.Object,java.lang.Object)
</function-signature>
</function>

<function>
<function-name>displayFirst</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayFirst(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>listExcludingSelectedOne</function-name>
<function-class>de.binaris.shoppinghelper.view.ViewUtils</function-class>
<function-signature>
java.util.List listExcludingSelectedOne(java.util.Collection,java.lang.Object)
</function-signature>
</function>
</facelet-taglib>

Mit entsprechenden Implementierungen der Tag-Funktionalitäten in der ViewUtils-Klasse unter Verwendung generischer Typen T und Collections List<T>.

Über Tags, TagLibs und die Erweiterung bestehender und die Definition eigener Tags gibt es bereits einen Blog-Eintrag und durch die soeben gezeigten Beispiele ist erkennbar, wie simpel es ist, eigene Tags für die .xhtml-Views heutiger JSF 2-Applikationen zu definieren und deren Utility functions-Signaturen im WEB-INF/classes/META-INF-Verzeichnis des classpaths als *name*-taglib.xml mit dem genannten XML Document-Header und dem jboss-forge-view namespace zu deklarieren (s.o. forge.taglib.xml).

Alle XML und XHTML-Dateien der Primefaces-Beispielapplikation finden sich im Deployment-Archiv shoppinghelper-primefaces.war im /target-Unterverzeichnissen des Projekts shoppinghelper-primefaces.

Konfiguration des JBoss WildFly Application Servers

Die Konfiguration des JBoss WildFly Application Servers 10 Final und JBoss WildFly Application Servers 11 Final bzw. neuerer Versionen (z. B. JBoss WildFly Application Server 12 Final) erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2 und 9 Final und betrifft die Datasource, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle in vorigen Blog-Einträgen bereits beschrieben. Wie die MySQL-Datenbank Inno DB 5.x zu konfigurieren ist, wurde ebenfalls in einem bereits vorhandenen Blog-Eintrag beschrieben.

Die Primefaces Beispiel-Applikation arbeitet über die RESTful WebService-Schnittstelle auf der MySQL-InnoDB. Für die Primefaces Beispiel-Applikation kann folgendes mit HeidiSQL erstellte MySQL 5.x Datenbank-Skript verwendet werden. Hier auch die Datasource Definition:

<datasource jndi-name=“java:jboss/datasources/ShoppinghelperDatasource“ pool-name=“ShoppinghelperDS“ enabled=“true“>
                    <connection-url>jdbc:mysql://localhost:3306/shoppinghelper</connection-url>
                    <driver>mysql-connector-java-5.1.34.jar</driver>
                    <transaction-isolation>TRANSACTION_READ_COMMITTED
                    </transaction-isolation>
                    <pool>
                        <min-pool-size>10</min-pool-size>
                        <max-pool-size>100</max-pool-size>
                        <prefill>true</prefill>
                    </pool>
                    <security>
                        <user-name>root</user-name>
                        <password>[das entsprechende Password]</password>
                    </security>
                    <statement>
                        <prepared-statement-cache-size>32</prepared-statement-cache-size>
                        <share-prepared-statements>true</share-prepared-statements>
                    </statement>
</datasource>

Die Hibernate-Konfiguration der WildFly Application Server ist ebenfalls leicht durchführbar und wurde bereits in einem Blog-Eintrag in diesem Blog hier beschrieben.

Die fertige Beispiel-Applikation shoppinghelper-primefaces kann leicht selbst deployt werden und kann ebenfalls hier auf dem Test-Server aufgerufen und danach ein kleiner Beispiel-Einkaufszettel erstellt werden.

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Hier das Beispiel-Projekte für die Eclipse Entwicklungsumgebung JBoss Developer Studio:

JBoss Developer Studio Projekt:

shoppinghelper-primefaces

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8. Über die Konfiguration von Hibernate, der Datasource und der MySQL 5.x Datenbank für den WildFly 9 und den WildFly 10 gibt es hier mehr Informationen in diesem Blog.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit, kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME anzupassen, damit dieser auf das tatsächliche JDK 8 – Verzeichnis des Server-Systems zeigt.

Hier auch der WildFly 11 als Zip-Archiv zum Download und Entpacken, für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8. Über die Konfiguration von Hibernate, der Datasource und der MySQL 5.x Datenbank wird ebenfalls auf bereits vorhandene Blog-Einträge hier verwiesen, da die Konfigurationen analog zum WildFly 9 und WildFly 10 durchzuführen sind.

Der fertig konfigurierte WildFly 11.Final mit vielen Beispiel-Applikationen der vorangegangenen Blog-Einträge ist hier zum Download verfügbar, kann runtergeladen, entpackt und gestartet werden. Auch hier ist nur der Pfad des JAVA_HOME anzupassen, damit dieser auf das tatsächliche JDK zeigt.

Die SQL-Skripte zum Anlegen und Befüllen der MySQL Beispiel-Datenbanken finden sich verlinkt in den Blog-Einträgen wieder, die hier durchsucht werden können, durch Eingabe eines Stichworts im Suche-Feld der Eingabemaske.

Die Primefaces Beispiel-Applikation dieses Blog-Eintrags kann auch selbst erneut deployt werden, ohne den bereits gestarteten WildFly Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv shoppinghelper-primefaces.war aus dem shoppinghelper-primefaces/target-Verzeichnis speichern

Wie man sieht, macht es viel Freude, JEE-Applikationen mit der vorteilhaften Primefaces-Technologie zu entwickeln und ebenfalls, zuvor die Faces-Beispielapplikationen mittels JBoss Forge auf dem Datenmodell zu erzeugen und weiterzuentwickeln.

Es ist also sehr wahrscheinlich, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces, Angular JS und Faces und den die JEE-Spezifikation implementierenden Technologien folgen.

Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test Driven Development mit Java, sowie ein schönes Neujahr und erfolgreiches Geschäftsjahr.

WebApps – RESTful mit Angular JS, JEE7, Java8, MySQL5 auf JBoss WildFly 9 und 10

Die Motivation

In dem bereits vorhandenen Blog-Eintrag über WebApps – vom Modell zur App, mit JSF 2.2, Angular JS, REST, JPA 2 wurde beschrieben, wie von einem Klassen-Diagramm (UML), genauer: Entity-Relationship-Diagramm hier, auch E-R Diagramm genannt, eine JEE7-Applikation erstellt werden kann. Diese JEE6- und JEE7-Beispielapplikationen hier liefern mit Hilfe von EJB 3-getriebenen Backends (Session Beans als DAOs, DAO-Pattern) unter Anbindung der JPA 2 Entities (Table-Per-Class-Pattern) mit Hilfe von DTOs über fachliche RESTful ServiceInterfaces oder per Microservices mit Docker, Spring Boot, siehe auch ServiceBroker-Pattern hier, die benötigten Daten zur Präsentation und Bearbeitung (Änderung, Löschen, Neuanlage) in den Web-Frontends, welche die WebServices konsumieren (ServiceConsumer-Pattern).

Eine dynamische Benutzeroberfläche, die sich an beliebige Auflösungen und Display-Größen responsiv anpasst, ist ebenfalls in der Beispiel-App enthalten, wie auch mit Hilfe des Angular JS Frameworks und des Bootstrap-Frameworks eine selbsterstellte Einkaufshilfe-Applikation (Einkaufsliste mit VK-Preisen) für einen simplen Einkaufszettel.

Die ausprogrammierte JEE7-Applikation shoppinghelper-angularjs mit dem Angular JS-Frontend und dem JEE-Backend mit den JPA 2 Entities kann einfach an verschiedene relationale Datenbanken (z. B. MySQL oder Postgres) angebunden werden, weshalb auch benötigte SQL-Export/-Import-Skripte zur Verfügung gestellt werden.

Die Beispiel-Applikation shoppinghelper-angularjs wird dann auf dem JBoss WildFly 10 Final Server (entspricht dem JBoss EAP 7-Server) und ebenfalls auf dem JBoss WildFly 9 deployt. Dies könnte jedoch problemlos auch auf einem WildFly 12 Server oder höher geschehen. Dabei ist die Verwendung dieser beiden JBoss WildFly JEE7-Application Server in einem Blog-Eintrag bereits beschrieben worden.

Erstellung der Beispiel Applikation auf dem Datenmodell

Mittels DBeaver wird das Klassendiagramm der Entities aus den Datenbank-Objekten erstellt und aus diesen, nach dem Anlegen der Datenbank per Hibernate Envers während dem WildFly 10 Undertow Deployment mittels JBoss Forge Eclipse PlugIn im JBoss Developer Studio erst der REST-WebService mit den Endpoints generiert und danach die Angular JS WebApp auf diesem RESTful WebService. Danach kann der ShoppingHelper ausprogrammiert werden.

E-R-diagram
(E-R-Diagramm der Beispiel-Datenbank „shoppinghelper“ ohne die MySQL-Sequence-Tabellen.)

Dabei darf positiv erwähnt werden, dass man mittels JBoss Forge eine saubere, stringente Architektur in die JEE7-Applikation einbringt, die beim Weiterentwickeln der App sehr hilfreich ist und sowohl die Entwicklungszeit-Performance als auch die Codequalität deutlich steigert. Ein großes Dankeschön an die JBoss Rockstars!

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

Es werden in der pom.xml die folgenden Maven Dependencies verwendet:

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Einsatz von CDI

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Über CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog. Die Aktivierung von CDI erfolgt mittels beans.xml im WEB-INF Verzeichnis.

Auch über die Architektur und verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in diesem Blog bzw. bereits vorausgegangenen Blog-Einträgen.

JBoss WildFly Konfiguration der MySQL-Anbindung

Die Konfiguration des JBoss WildFly Application Servers 10 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2 und 9 Final und betrifft die Datasource, die MySQL-Datenbank Inno DB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle in diesem Blog bereits beschrieben. Die Anbindung an die relationalen Datenbanken Postgres 9 und MySQL 5 ist in einem Blog-Eintrag bereits beschrieben.

Je nach verwendeter Datenbank kann in der persistence.xml im Deployment-Paket der Beispiel-App shoppinghelper-angularjs.war ein anderer Datenbank-Dialekt eingetragen werden:

Hier die /META-INF/persistence.xml der Beispiel-Applikation für MySQL5:

<xml version=“1.0″ encoding=“UTF-8″?>
<persistence version=“2.0″ xmlns=“http://java.sun.com/xml/ns/persistence“
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“

xsi:schemaLocation=“http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd“>

   
<persistence-unit name=“ShoppinghelperPU“>

<provider>org.hibernate.ejb.HibernatePersistence</provider>

  <jta-data-source>

    java:jboss/datasources/ShoppinghelperDatasource
  </jta-data-source>

  <properties>

    <property name=“hibernate.hbm2ddl.auto“ value=“none“/>

    <property name=“hibernate.show_sql“ value=“false“/>

  </properties>

</persistence-unit>

</persistence>

Dafür wird je Datenbank die folgende Datasource benötigt und ist in
der standalone/configuration/standalone.xml des JBoss WildFly Servers unter den <datasources> einzutragen:

Für MySQL 5:

<datasource  jndi-name=“java:jboss/datasources/ShoppinghelperDatasource“
pool-name=“ShoppinghelperDS“
enabled=“true“>
<connection-url>jdbc:mysql://localhost:3306/shoppinghelper</connection-url>
<driver>mysql-connector-java-5.1.34.jar</driver>

<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>

<pool>
  <min-pool-size>10</min-pool-size>

  <max-pool-size>100</max-pool-size>

  <prefill>true</prefill>

</pool>

<security>
  <user-name>root</user-name>

  <password>das entsprechende Passwort</password>

</security>

<statement>
  <prepared-statement-cache-size>32</prepared-statement-cache-size>

  <share-prepared-statements>true</share-prepared-statements>

</statement>
   
</datasource>

Für die erfolgreiche Anbindung der soeben deklarierten Datasoures werden die Datenbank-Treiber auf Modul-Ebene des JBoss WildFly konfiguriert, wie in einem Blog-Eintrag bereits beschrieben.

Damit die Datasources die soeben konfigurierten Treiber auch finden, werden diese in der /standalone/configuration/standalone.xml bei den <datasources>deklariert:

Für MySQL 5:

<driver name=“mysql-connector-java-5.1.34.jar“
module=“com.mysql“>

<driver-class>com.mysql.jdbc.Driver</driver-class>
  
  <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

  </xa-datasource-class>

</driver>

Hier auch der komplette MySQL-Export der Beispiel-Applikation shoppinghelper-angularjs:

Für MySQL 5: export_shoppinghelper_database_sql.txt

Die fertige Beispiel-Applikation shoppinghelper-angularjs
kann zusammen mit den anderen Beispiel-Applikationen hier betrachtet werden (MySQL5 Server auf Linux).

Hier das Beispiel-Projekte für die Eclipse-Entwicklungsumgebung JBoss Developer Studio:

shoppinghelper-angularjs

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10 Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, wie in diesem Blog-Eintrag hier beschrieben wurde, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt und ebenfalls der Pfad für das log-Dateien Verzeichnis in der logging.properties. Die Angular JS Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10 Application Server überhaupt stoppen oder erneut starten zu müssen.

Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments das Archiv shoppinghelper-angularjs.war aus dem shoppinghelper-angularjs/target-Verzeichnis speichern

Sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann, falls der Default-Port fürs http-Protokoll nicht anders konfiguriert wurde, unter folgender Url aufgerufen werden:

http://localhost:8080/shoppinghelper-angularjs

Fazit: Wie man sieht, macht es einfach viel Freude, z.B. mittels JBoss Forge und JPA 2 Entities die responsiven Angular JS Apps auf dem Datenmodell zu erzeugen und weiterzuentwickeln oder eben robuste, komfortable, Browser-basierte Primefaces 6-Fontends, sei es nun für mobile IMS (Informations-Management-Systeme) oder für PIMS (Product Information Management Systems) und diese auf einem die JEE7-Spezifikation erfüllenden JBoss (EAP oder z. B. WildFly 9, WildFly 10) Server verfügbar zu machen. Somit besteht durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces 6, Angular JS und Bootstrap, sowie den die JEE7-Spezifikation implementierenden WebService-und Backend-Technologien folgen. Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test-Driven Development mit Java, sowie noch eine schöne Vorweihnachtszeit und ein schönes Weihnachtsfest.

WebApps – Primefaces 6, REST, JEE7, Angular JS auf JBoss WildFly 9 und 10

Die Motivation

Im Blog-Eintrag über WebApps – Responsiv mit Angular JS, REST, JEE7, Java 8 auf JBoss WildFly 9 und 10 hier wurde beschrieben, wie aus einem mittels Eclipse-PlugIn Umlet erstellten Entity-Relationship-Diagramm schrittweise eine responsive JEE7-Applikation mit Angular JS User Interface entsteht. Die  benötigten Daten zur Präsentation und Bearbeitung (Änderung, Löschen, Neuanlage) in den Web-Frontends liefern bei den JEE6- und JEE7-Beispielapplikationen hier EJB 3-getriebenen Backends (Session Beans als DAOs, „DAO“-Pattern) unter Anbindung der JPA 2 Entities („Table-Per-Class“-Pattern) mit Hilfe von DTOs über fachliche RESTful WebService-Endpoints zwecks besserer Steuerung der vertikalen Skalierbarkeit.

Die Beispiel-Applikation fridge-primefaces verwendet die JSF2-Implementierung Primefaces 6 und die Beispiel-Applikation fridge-angularjs dieses Blog-Eintrags verwendet das Google Angular JS Framework. Beide Beispiel-Applikationen werden auf dem JBoss WildFly 10 Application  Server (entspricht dem JBoss EAP 7-Server) deployt.

Dabei ist die Verwendung der beiden JBoss WildFly JEE7-Application Server in diesem Blog-Eintrag bereits in diesem Blog beschrieben worden. Das Durchsuchen, und die Nutzung einer Pagination der Ergebnisliste ist ebenfalls in den Beispiel-Applikationen enthalten, wie auch mit Hilfe des Angular JS Frameworks und des Bootstrap-Frameworks eine dynamische Benutzeroberfläche, die sich in der Beispiel-Applikation fridge-angularjs an beliebige Auflösungen und Display-Größen responsiv anpasst. Hier nun das E-R-Diagramm der Kühlschrank-Datenbank:

E-R-diagram
(E-R-Diagramm der Beispiel-Datenbank „fridgedb“ ohne die MySQL-Sequence-Tabellen.)

Weiterhin gibt es in der Primefaces 6 Beispiel-App fridge-primefaces die Möglichkeit zum Excel-Export in einer simplen Reporting-View, welche diejenigen Artikel im Kühlschrank als erstes listet, deren MHD als nächstes expiriert.

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

Dabei werden in der pom.xml die folgenden Maven Dependencies verwendet:

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA, EJB, Primefaces, POI) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

– primefaces-6.0
– poi-3.7

– cdi-api_1.1

Über CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog. Die Aktivierung von CDI erfolgt mittels beans.xml im WEB-INF Verzeichnis.

Auch über die Architektur und verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in bereits vorausgegangenen Blog-Einträgen.

Der aktivierte Link zum DataExporter verwendet die folgende, oben genannte Apache POI-Dependency hier.

Viele sehr gute Beispiele zur Konfiguration der DataExporter (Excel, PDF, CSV, XML) finden sich im Primefaces Showcase hier.

Für die Reporting-View der demnächst expirierenden Artikel in der Beispiel-Applikation fridge-primefaces wird die ArticleBean um die folgende Methode erweitert:

public void expiringItems() {

                   CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();

                   CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);

                   Root<Article> root = countCriteria.from(Article.class);

                   countCriteria = countCriteria.select(builder.count(root)).where(

                                                  getSearchPredicates(root));

                   this.count = this.entityManager.createQuery(countCriteria)

                                                  .getSingleResult();

                   CriteriaQuery<Article> criteria = builder.createQuery(Article.class);

                   root = criteria.from(Article.class);

                   criteria.orderBy(builder.asc(root.get(„expiryDateTime“)));

                   TypedQuery<Article> query = this.entityManager.createQuery(criteria

                                                  .select(root).where(getSearchPredicates(root)));

                   query.setFirstResult(this.page * getMaxPageSize()).setMaxResults(

                                                  getMaxPageSize());

                   this.pageItemsExpiring = query.getResultList();

   }

In der Report-View (article/report.xhtml), erreichbar über den ‚Report‘-Menüpunkt, ist eine Report Excel-Datei zum Download erhältlich. Ein Klick auf das Buch-Symbol startet den Download der Datei ‚articles_expiring_soon.xls‘ mit den Columns (Name, Category, Expiry Date) über die bald das MHD erreichenden Artikel im Kühlschrank aus der ‚Articles expiring soon‘-Übersicht der Report-View .

Die Konfiguration des JBoss WildFly Application Servers 10 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2 und 9 Final und betrifft die Datasource, die MySQL-Datenbank InnoDB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle, in einem Blog-Eintrag dieses Blogs bereits beschrieben.

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Hier auch die /META-INF/persistence.xml der Beispiel-Applikation:

<xml version=“1.0″ encoding=“UTF-8″?>
<persistence version=“2.0″ xmlns=“http://java.sun.com/xml/ns/persistence“
    xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“ xsi:schemaLocation=“http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd“>
<persistence-unit name=“FridgecontentPU“>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>
    java:jboss/datasources/FridgecontentDatasource
</jta-data-source>
<properties>
    <property name=“hibernate.hbm2ddl.auto“ value=“none“/>
    <property name=“hibernate.show_sql“ value=“false“/>
</properties>
</persistence-unit>
</persistence>

Dafür wird die folgende Datasource benötigt und ist in der standalone/configuration/standalone.xml des JBoss WildFly Servers unter den <datasources> einzutragen:

<datasource jndi-name=“java:jboss/datasources/FridgecontentDatasource“ pool-name=“FridgecontentDS“
    enabled=“true“>
    <connection-url>jdbc:mysql://localhost:3306/fridgecontent</connection-url>
    <driver>mysql-connector-java-5.1.34.jar</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
    <pool>
    <min-pool-size>10</min-pool-size>
    <max-pool-size>100</max-pool-size>
    <prefill>true</prefill>
    </pool>
    <security>
    <user-name>root</user-name>
    <password>das entsprechende Passwort</password>
    </security>
    <statement>
    <prepared-statement-cache-size>32</prepared-statement-cache-size>
    <share-prepared-statements>true</share-prepared-statements>
    </statement>
</datasource>

Hier auch das mit HeidiSQL erstellte Datenbank-Skript (SQL Export) zum Anlegen der Datenbank fridgecontent.

Die fertigen Beispiel-Applikationen fridge-primefaces und fridge-angularjs können zusammen mit den anderen Beispiel-Applikationen hier betrachtet werden.

Hier die Beispiel-Projekte für die Eclipse-Entwicklungsumgebung JBoss Developer Studio:

fridge-primefaces

fridge-angularjs

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung zum Start des WildFly 10 Application Servers ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt. Für die Weiterentwicklung, den Build und das Deployment des Projekts ist mindestens Java 6 und Maven 3 erforderlich. Als Entwicklungsumgebung wurde Eclipse Form des “JBoss Developer Studios“ verwendet .

Weiterhin die FireFox WebDeveloper IDE/Tools. Als Datenbank wurde MySQL InnoDB 5.x eingesetzt, hier kann aber auch leicht PostgreSQL,Oracle oder auch gerne eine andere relationale Open Source-Datenbank verwendet werden. Der WildFly 10 verwendet als Java Runtime Java 8 oder höher, weshalb jeder WildFly Application Server auf dem Linux-Server für die Beispiel-Applikationen seine eigene JVM bekam, (WildFly 9 läuft auf Java 7, WildFly 10 auf Java 8). Hierfür wird im Startskript standalone.sh einfach das benötigte JAVA_HOME gesetzt. Auch bekam jede WildFly Application Server-Instanz einen eigenen Port, auf dem die Beispiel-Applikationen erreichbar sind.

Die Primefaces 6 Beispiel-Applikation dieses Blog-Eintrags kann selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10 Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv fridge-primefaces.war aus dem fridge-primefaces/target-Verzeichnis speichern

Sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann aufgerufen werden:

http://localhost:8080/fridge-primefaces

Die Angular JS Beispiel-Applikation dieses Blog-Eintrags kann selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10 Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv logisticsadmin-angularjs.war aus dem fridge-angularjs/target-Verzeichnis speichern

Sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann aufgerufen werden:

http://localhost:8080/fridge-angularjs

Wie man sieht, macht es einfach Spaß, die responsiven Angular JS-Beispielapplikationen mittels JBoss Forge auf dem Datenmodell zu erzeugen und weiterzuentwickeln und z. B. mit der Primefaces 6-Version komfortable JEE7-Beispielapplikationen auf einer erweiterten REST-Schnittstelle zu entwickeln.

Es besteht also durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Angular JS, Bootstrap und den die JEE7-Spezifikation implementierenden Backends und darauf basierenden WebServices und folgen. Im nächsten Blog-Eintrag soll mittels einer ’shoppinghelper‘-Beispielapplikation dann der Kühlschrank der soeben besprochenen Beispielapplikation fridge-primefaces tatsächlich mit ein paar Einkaufsartikeln befüllt werden. Man darf also weiterhin auf den ersten Advent gespannt sein. Allen interessierten Leserinnen und Lesern viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test Driven Development mit Java.

WebApps – vom Modell zur App, mit JSF 2.2, Angular JS, REST, JPA 2, JBoss WildFly 10

Die Motivation

In einem vorigen Blog-Eintrag wurde die Migration der Beispiel-Applikationen MyFaces JSF 2.2 Beispiel-Applikation greetingcardsadmin auf die sehr vorteilhafte Primefaces-Technologie beschrieben. In diesem Blog-Eintrag soll nun einmal beschrieben werden, wie leicht es ist, mit dem Datenmodell eine Beispiel-Webapplikation zu erstellen. Dabei werden als Basis-Frontendtechnologien JSF 2.2, Angular JS und Primefaces verwendet.

Über die JSF Technologie gibt es bereits einen Blog-Eintrag in diesem Blog. Die soeben referenzierte Applikation greetingcardsadmin wurde bereits zur Primefaces-Applikation greetingcardsadmin-primefaces migriert und unterstützt den Einsatz der robusten, aber stets elegant gestylten, etablierten, optimalen Primefaces Frontend-Technologie und wurde testweise auf dem JBoss WildFly 10 Final deployt. Dabei ist die Verwendung der beiden neuen JBoss WildFly JEE7-Application Server in diesem Blog bereits beschrieben worden. Hatten die MyFaces-Beispiel-Applikationen noch extra Suchfelder und Schwächen bei der Sortierung der Ergebnislisten, so bieten nach der Umstellung die Primefaces-Views das Durchsuchen, Sortieren und eine wählbare Pagination der Ergebnisliste bereits als Komfortfunktionen der Standard Primefaces-Funktionalitäten an.

Erstellung der Beispiel Applikation aus dem Datenmodell

Wie die Applikationen recipeadmin und  recipeadmin-angularjs mittels JBoss Forge erstellt werden können, ist bereits analog in einem Blog-Eintrag für die Applikationen greetingcardsadmin und greetingcardsadmin-angularjs beschrieben worden. Hier das E-R-Diagramm der Rezepte-Datenbank:

E-R-diagram
(E-R-Diagramm der Beispiel-Datenbank „recipedb“ ohne die MySQL-Sequence-Tabellen.)

Wie in der pom.xml des jeweiligen Beispielprojekts nach erfolgtem Download erkennbar ist, wurden auch in den Applikationen recipeadmin-primefaces und recipeadmin-angularjs die folgenden JEE 7 Artefakte für den JBoss WildFly identifiziert, verwendet und importiert:

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Einsatz von CDI

Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF Verzeichnis.

Auch über die Architektur und verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in vorausgegangenen Blog-Einträgen dieses Blogs.

Primefaces:

Hat man erst einmal die JPA 2.2 Entities definiert und mittels JBoss Forge die Beispiel-Webapplikationen für die ausgewählte Frontend-Technologie (Angular JS oder Faces) erzeugt und rebrandet, ist die Migration der Faces Beispiel-Applikation leicht durchführbar und wurde bereits beschrieben.

Die Einbindung der erforderlichen Primefaces-UI-Bibliothek primefaces-5.2.jar erfolgt dabei ganz simpel übers Hinzufügen der entsprechenden Maven Primefaces Dependency in der pom.xml.

    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>5.2</version>
    </dependency>

Allein durch Änderung dieses Eintrags in der pom.xml kann jederzeit auch auf die gewünschte Primefaces-Version umgestellt werden. Bei der Umstellung der ca. 30 XHTML-Viewsm von MyFaces auf Primefaces sind der Primefaces Showcase und der Primefaces Users Guide (PDF) sehr hilfreich. Der größte Aufwand der Migration besteht hierbei im Austausch der Standard MyFaces-Tags (h: Präfix) durch die Primefaces-Tags (p: Präfix) ist also für die simplen Beispiel-Applikationen leicht umsetzbar. Ebenfalls wurde das sehr gute bei DZone erhältliche PDF zur JSTL (Java Server Pages Template Library) verwendet und zwei weitere Dokumente über genauere Details zum JSF 2-Lifecycle, die sich hier und hier zum Download finden.

Aktiviert werden die Primefaces-Tags in der gewünschten XHTML-Seite mittels folgendem Eintrag im Dokumenten-Kopf der XHTML-View xmlns:p=“http://primefaces.org/ui und die zusätzliche Funktionalität der JSTL-Tags wird entsprechend, wie in den MyFaces-Views auch, per xmlns:c=“http://java.sun.com/jsp/jstl/core
verfügbar gemacht.

Durch die Verwendung von CDI 1.1 haben die JSF ManagedBeans stattdessen die Annotation @Named und bleiben, bis auf ein paar Helper-Methoden zur Ausgabeformatierung, nahezu identisch zu den ManagedBeans der MyFaces-Applikation recipeadmin. Weitere Informationen zu JSF 2.2, CDI und Primefaces finden sich auch hier im Blog von Balus C.

forge.taglib.xml:

Die in den Beispiel-Applikationen verwendete, moderne Forge TagLib kann sehr einfach erweitert werden, indem eine zusätzlich benötigte Methode in der ViewUtils-Klasse ergänzt wird und deren Signatur in der forge.taglib.xml im Verzeichnis src/main/webapp/WEB-INF/classes/META-INF/ bekanntgemacht wird. Die TagLib wird in der entsprechenden .xhtml-View über den Eintrag xmlns:forgeview=“http://jboss.org/forge/view“ mit dem Prefix forgeview unter der DOCTYPE-Definition im ui:composition eingebunden und mittels forgeview-Prefix per Expression Language (EL) an der gewünschten Stelle mit den richtigen Input-Parametern aufgerufen. Hier die forge.taglib.xml für die Beispiel-Applikationen recipeadmin-primefaces und recipeadmin:

<?xml version=“1.0“ encoding=“UTF-8“?>
<!DOCTYPE facelet-taglib PUBLIC “-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN“ “http://java.sun.com/dtd/facelet-taglib_1_0.dtd“>
<facelet-taglib xmlns=“http://java.sun.com/JSF/Facelet“>
<namespace>http://jboss.org/forge/view</namespace>
<function>
<function-name>asList</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.util.List asList(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>display</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.lang.String display(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>count</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
int count(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>displayShort</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayShort(java.lang.Object, int)
</function-signature>
</function>

<function>
<function-name>displayRange</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayRange(java.lang.Object,java.lang.Object)
</function-signature>
</function>

<function>
<function-name>displayFirst</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayFirst(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>listExcludingSelectedOne</function-name>
<function-class>de.binaris.recipeadmin.view.ViewUtils</function-class>
<function-signature>
java.util.List listExcludingSelectedOne(java.util.Collection,java.lang.Object)
</function-signature>
</function> 
</facelet-taglib>

Mit entsprechenden Implementierungen der Tag-Funktionalitäten in der ViewUtils-Klasse unter Verwendung generischer Typen T und Collections List<T>.

Über Tags, TagLibs und die Erweiterung bestehender und die Definition eigener Tags gibt es
bereits Informationen in diesem Blog und durch die soeben gezeigten Beispiele ist erkennbar, wie simpel es ist, eigene Tags für die .xhtml-Views heutiger JSF-Applikationen zu definieren.

Alle XML und XHTML-Dateien der Primefaces-Beispielapplikation finden sich im Deployment-Archiv  recipeadmin-primefaces.war im  /target-Unterverzeichnissen des Projekts recipeadmin-primefaces.

Die Konfiguration des JBoss WildFly Application Servers 10 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2 und 9 Final und betrifft die Datasource, die MySQL-Datenbank Inno DB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle in diesem Blog bereits beschrieben worden.

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Die Primefaces Beispiel-Applikation arbeitet über die RESTful WebService-Schnittstelle auf derselben Datenbanken, wie die MyFaces-Applikation recipeadmin und die AngularJS Beispiel-Applikation recipeadmin-angularjs, weshalb für die Primefaces Beispiel-Applikationen dasselbe Datenbank-Skript verwendet werden kann.

Die fertigen Beispiel-Applikationen recipeadmin-primefaces, recipeadmin und recipeadmin-angularjs können hier betrachtet werden.

Hier die Beispiel-Projekte für die Entwicklungsumgebungen JBoss Developer Studio und intelliJ 15:

JBoss Developer Studio Projekte:

recipeadmin

recipeadmin-angularjs

recipeadmin-primefaces

intelliJ Projekte:

recipeadmin

recipeadmin-angularjs

recipeadmin-primefaces

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt.

Die Angular JS Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10  Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv recipeadmin-angularjs.war aus dem recipeadmin-angularjs/target-Verzeichnis speichern

Sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann entweder hier oder unter der folgenden Url aufgerufen werden:

http://localhost:8080/recipeadmin-angularjs

Die Primefaces Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10  Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv recipeadmin-primefaces.war aus dem recipeadmin-primefaces/target-Verzeichnis speichern

Die Faces Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 10  Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments

– das Archiv recipeadmin-primefaces.war aus dem recipeadmin /target-Verzeichnis speichern

Wie man sieht, macht es viel Freude, die XHTML-Views der beschriebenen Beispiel-Applikation von MyFaces oder RichFaces auf die vorteilhafte Primefaces-Technologie umzustellen und ebenfalls, zuvor die Angular JS und Faces-Beispielapplikationen mittels JBoss Forge auf dem Datenmodell zu erzeugen und weiterzuentwickeln.

Hier auch das Beispiel-Datenmodell als DB-Export (export_database_recipeadmin_sql.pdf).

Es besteht also durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces, Angular JS und Faces  und den die JEE7-Spezifikation implementierenden Technologien folgen. Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test Driven Development mit Java, sowie schonmal schöne Herbstferien und „Happy Halloween“.

WebApps – Migration von MyFaces zu Primefaces, REST, JPA 2, JEE7 auf JBoss WildFly 10

Die Motivation

Dem vorigen Blog-Eintrag zur Migration von RichFaces oder MyFaces-Implementierungen auf Primefaces folgend soll in diesem Blog-Eintrag hier die Migration der MyFaces JSF 2.2 Beispiel-Applikation library auf die sehr vorteilhafte Primefaces-Technologie beschrieben werden. Die migrierte library-primefaces ermöglicht dann die leichte Pflege von und die Suche nach Ausleihen von Kunden, d. h. diese Beispielapplikation ermöglicht die Bearbeitung und Präsentation der Stammdaten (Kunden), Bestandsdaten (E-Books) und Bewegungsdaten (Ausleihen) einer Bibliothek. Hatten die MyFaces-Beispiel-Applikationen noch extra Suchfelder und Schwächen bei der Sortierung der Ergebnislisten, bieten nach der Umstellung die Primefaces-Views das Durchsuchen, Sortieren und eine wählbare Pagination der Ergebnisliste bereits als Komfortfunktionen der robusten, aber stets elegant gestylten, etablierten, optimalen Primefaces Frontend-Technologie und deren Standard Primefaces-Funktionalitäten an.

Die Beispiel-Applikation library-primefaces wird dann wird dann auf den JBoss WildFly 9 Final, WildFly 10 Final und WildFly 11 Final Servern deployt und kann hier getestet werden. Die Verwendung der JBoss WildFly JEE7-Application Server wurde in diesem Blog bereits einmal beschrieben. Dabei liefern die JEE6- und JEE7-Beispielapplikationen hier mit Hilfe eines EJB 3-getriebenen Backends (Session Beans als DAOs, „DAO“-Pattern) unter Anbindung der JPA 2 Entities („Table-Per-Class“-Pattern) mit Hilfe von DTOs über fachliche RESTful ServiceInterfaces oder per Microservices mit Docker, Spring Boot, siehe auch „ServiceBroker“-Pattern hier, die benötigten Daten zur Präsentation und Bearbeitung (Änderung, Löschen, Neuanlage) in den Web-Frontends, welche die WebServices konsumieren („ServiceConsumer“-Pattern).

Erstellung der Beispiel Applikation auf dem Datenmodell

Mittels DBeaver wird das Klassendiagramm der Entities aus den Datenbank-Objekten erstellt und aus diesen, nach dem Anlegen der Datenbank per Hibernate Envers während dem WildFly 10 Undertow Deployment mittels JBoss Forge Eclipse PlugIn im JBoss Developer Studio erst der REST-WebService mit den Endpoints generiert und danach die Primefaces 6 WebApp auf diesem RESTful WebService. Hier das E-R-Diagramm der Bibliotheks-Datenbank:

E-R-diagram
(E-R-Diagramm der Beispiel-Datenbank „library“ ohne die MySQL-Sequence-Tabellen.)

Dabei darf positiv erwähnt werden, dass man mittels JBoss Forge eine saubere, stringente Architektur in die JEE7-Applikation einbringt, die beim Weiterentwickeln der App sehr hilfreich ist und sowohl die Entwicklungszeit-Performance als auch die Codequalität deutlich steigert. Danke, JBoss Rockstars!

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Es werden in der pom.xml die folgenden Maven Dependencies identifiziert, verwendet und importiert:

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Dabei erfolgt die Aktivierung von CDI mittels beans.xml im WEB-INF Verzeichnis.

über die Architektur und die verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in diesem Blog-Eintrag hier und bereits vorausgegangenen Blog-Einträgen.

Einsatz von Primefaces

Die Einbindung der erforderlichen Primefaces-UI-Bibliothek primefaces-6.0.jar erfolgt ganz simpel über die entsprechende Maven Primefaces Dependency in der pom.xml. Es ist also kein extra /lib-Verzeichnis und keine build.xml für einen Ant Build-Prozess erforderlich, denn es wird einfach der folgende Eintrag zur pom.xml für den Maven Build hinzugefügt:

<dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>6.0</version>
    </dependency>

Allein durch Änderung dieses Eintrags in der pom.xml kann jederzeit auch auf die gewünschte Primefaces-Version umgestellt werden. Bei der Umstellung der ca. 80 XHTML-Views von MyFaces auf Primefaces sind der Primefaces Showcase und der Primefaces Users Guide (PDF) sehr hilfreich. Der größte Aufwand der Migration besteht hierbei im Austausch der Standard MyFaces-Tags (h: Präfix) durch die Primefaces-Tags (p: Präfix) ist also für die simplen Beispiel-Applikationen leicht umsetzbar. Ebenfalls wurde das sehr gute bei DZone erhältliche PDF zur JSTL (Java Server Pages Template Library) verwendet und zwei weitere Dokumente über genauere Details zum JSF 2-Lifecycle, die sich hier und hier zum Download finden.

Aktiviert werden die Primefaces-Tags in der gewünschten XHTML-Seite mittels folgendem Eintrag im Dokumenten-Kopf der XHTML-View xmlns:p=“http://primefaces.org/ui und die zusätzliche Funktionalität der JSTL-Tags wird entsprechend, wie in den MyFaces-Views auch, per xmlns:c=“http://java.sun.com/jsp/jstl/core verfügbar gemacht.

Durch die Verwendung von CDI 1.1 haben die JSF ManagedBeans stattdessen die Annotation @Named und bleiben, bis auf ein paar Helper-Methoden zur Ausgabeformatierung, nahezu identisch zu den ManagedBeans der MyFaces-Applikation library.

Weitere Informationen zu JSF 2.2, CDI und dem @ViewScope gibt es auch auf stackoverflow.com. Die dort beschriebenen Themen finden sich auch in den ManagedBeans der JSF 2.2 Beispiel-Applikation wieder.

Für die Migration von RichFaces 4 Applikationen auf Primefaces gibt es in diesem Blog hier ebenfalls eine sehr nützliche Anleitung. Es wird hierzu auch noch einen Blog-Eintrag geben. Hier noch eine weitere SPA (Single Page Appliction) Beispiel-Applikation, bei der das Testen des Backends mittels Arquillian und das integrative Testen des Frontends mittels Selenium (Workflow und UX Elemente) und QUnit (Javascript-Framework zum Test-Aufruf der REST-Endpoints aus einer Browser-View) ausführlich beschrieben ist.

Weitere Informationen zu JSF 2.2, CDI und Primefaces finden sich auch hier im Blog von Balus C. Detail-Informationen zur JavaScript-Bibliothek jQuery, die vom Primefaces-Framework sehr intensiv verwendet wird, finden sich hier: jquery.org

forge.taglib.xml:

Die in den Beispiel-Applikationen verwendete, moderne Forge TagLib kann sehr einfach erweitert werden, indem die zusätzlich benötigten Methoden in der ViewUtils-Klasse ergänzt werden und deren Signaturen in der forge.taglib.xml im Verzeichnis src/main/webapp/WEB-INF/classes/META-INF/ bekanntgemacht werden, also genau wie bei klassischen TagLibs mit dem Unterschied, dass die verwendeten Tags innerhalb der Applikation in der ViewUtils-Klasse implementiert werden, was für die Applikation zusätzliche Sicherheit bringt. Diese Tags sind für jede Applikation sehr leicht adaptierbar und können genau auf die Applikation zugeschnitten werden. Die TagLib wird in der entsprechenden .xhtml-View über den Eintrag xmlns:forgeview=“http://jboss.org/forge/view“ mit dem Prefix forgeview unter der DOCTYPE-Definition im ui:composition eingebunden und mittels forgeview-Prefix per Expression Language (EL) an der gewünschten Stelle mit den richtigen Input-Parametern aufgerufen. Hier die forge.taglib.xml für die Beispiel-Applikation library-primefaces:

<?xml version=“1.0“ encoding=“UTF-8“?>
<!DOCTYPE facelet-taglib PUBLIC “-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN“ “http://java.sun.com/dtd/facelet-taglib_1_0.dtd“>
<facelet-taglib xmlns=“http://java.sun.com/JSF/Facelet“>
<namespace>http://jboss.org/forge/view</namespace>
<function>
<function-name>asList</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.util.List asList(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>display</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.lang.String display(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>count</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
int count(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>displayShort</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayShort(java.lang.Object, int)
</function-signature>
</function>

<function>
<function-name>displayRange</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayRange(java.lang.Object,java.lang.Object)
</function-signature>
</function>

<function>
<function-name>displayFirst</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayFirst(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>listExcludingSelectedOne</function-name>
<function-class>de.binaris.library.view.ViewUtils</function-class>
<function-signature>
java.util.List listExcludingSelectedOne(java.util.Collection,java.lang.Object)
</function-signature>
</function> 
</facelet-taglib>

Mit entsprechenden Implementierungen der Tag-Funktionalitäten in der ViewUtils-Klasse unter Verwendung generischer Typen T und Collections List<T>.

Durch die soeben gezeigten Beispiele ist erkennbar, wie simpel es ist, eigene Tags für die .xhtml-Views heutiger JSF 2-Applikationen zu definieren und deren Utility functions-Signaturen im WEB-INF/classes/META-INF-Verzeichnis des classpaths als *name*-taglib.xml mit dem genannten XML Document-Header und dem jboss-forge-view namespace zu deklarieren (s.o. forge.taglib.xml).

Alle XML und XHTML-Dateien der Primefaces-Beispielapplikation finden sich im Deployment-Archiv library-primefaces.war im /target-Unterverzeichnissen des Projekts library-primefaces.

WildFly Anbindung an MySQL5, Postgres 9 und deklarative WildFly 11 Hibernate-Konfiguration

Die Konfiguration des JBoss WildFly Application Servers 11 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2, 9 und 10 Final und betrifft die Datasource, die MySQL-Datenbank Inno DB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle ein wichtiges Thema, welches noch genauer beschrieben werden wird. Die Anbindung an die relationale Datenbank MySQL 5 erfolgt übrigens analog für die JBoss Application Server WildFly 8.1, 8.2, 9, 10 und WildFly 11.

Je nach verwendeter Datenbank kann in der persistence.xml im Deployment-Paket der Beispiel-App library-primefaces ein entsprechender Dialekt eingetragen werden:

Hier die /META-INF/persistence.xml der Beispiel-Applikation für MySQL5:

<xml version=“1.0″ encoding=“UTF-8″?>
    <persistence version=“2.0″ xmlns=“http://java.sun.com/xml/ns/persistence“  xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
    xsi:schemaLocation=“http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd“>
    <persistence-unit name=“LibraryPU“>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>
            java:jboss/datasources/LibraryDatasource
    </jta-data-source>
    <properties>
    <property name=“hibernate.hbm2ddl.auto“ value=“none“/>
    <property name=“hibernate.show_sql“ value=“false“/>
    </properties>
    </persistence-unit>
    </persistence>

Dafür wird je Datenbank die folgende Datasource benötigt und ist in der standalone/configuration/standalone.xml des JBoss WildFly Servers unter den <datasources> einzutragen:

Für MySQL 5:

    <datasource jndi-name=“java:jboss/datasources/LibraryDatasource“ pool-name=“LibraryDS“
    enabled=“true“>
    <connection-url>jdbc:mysql://localhost:3306/library</connection-url>
    <driver>mysql-connector-java-5.1.34.jar</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
    <pool>
    <min-pool-size>10</min-pool-size>
    <max-pool-size>100</max-pool-size>
    <prefill>true</prefill>
    </pool>
    <security>
    <user-name>root</user-name>
    <password>das entsprechende Passwort</password>
    </security>
    <statement>
    <prepared-statement-cache-size>32</prepared-statement-cache-size>
    <share-prepared-statements>true</share-prepared-statements>
    </statement>
    </datasource>

Für Postgres 9:

    <datasource jndi-name=“java:jboss/datasources/LibraryDatasource“ pool-name=“LibraryDS“ enabled=“true“>
    <connection-url>jdbc:postgresql://localhost:5432/library</connection-url>
    <driver>postgresql-9.4.1209.jar</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED
    </transaction-isolation>
    <pool>
    <min-pool-size>10</min-pool-size>
    <max-pool-size>100</max-pool-size>
    <prefill>true</prefill>
    </pool>
    <security>
    <user-name>root</user-name>
    <password>[das entsprechende Password]</password>
    </security>
    <statement>
    <prepared-statement-cache-size>32</prepared-statement-cache-size>
    <share-prepared-statements>true</share-prepared-statements>
    </statement>
    </datasource>

Für die erfolgreiche Anbindung der soeben deklarierten Datasoures werden die Datenbank-Treiber auf Modul-Ebene des JBoss WildFly konfiguriert, wie hier für den MySQL JDBC4 Treiber beispielhaft auf dem WildFly 8.2 beschrieben wurde.

Damit die Datasources die soeben konfigurierten Treiber auch finden, werden diese in der /standalone/configuration/standalone.xml bei den <datasources> deklariert:

Für Postgres 9:

<driver name=“postgresql-java-9.4.1209.jar“ module=“org.postgresql“>
   <driver-class>org.postgresql.Driver</driver-class>
   <xa-datasource-class>org.postgresql.xa.PGXADataSource
   </xa-datasource-class>
</driver>

Für MySQL 5:

<driver name=“mysql-connector-java-5.1.34.jar“ module=“com.mysql“>
   <driver-class>com.mysql.jdbc.Driver</driver-class>
   <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
   </xa-datasource-class>
</driver>

Hier auch der komplette MySQL-Export der Beispiel-Applikation library-primefaces:

Für MySQL 5: export_library_database.sql

Die Hibernate-Konfiguration des WildFly 11 Final Servers ist ebenfalls leicht durchführbar. Hierfür werden im WildFly11 Server-Verzeichnis wildfly-11.0.0.Final/modules/system/layers/base/org/hibernate/main folgende Dateien hinterlegt:

– hibernate-core-5.1.10.Final.jar
– hibernate-entitymanager-5.1.10.Final.jar
– hibernate-envers-5.1.10.Final.jar
– hibernate-java8-5.1.10.Final.jar
– jipijapa-hibernate5-11.0.0.Final.jar
– module.xml
– mysql-connector-java-5.1.34.jar
– mysql-connector-java-5.1.34.jar.index

Die fertige Beispiel-Applikation library-primefaces kann zusammen mit den anderen Beispiel-Applikationen hier betrachtet werden (MySQL5 Server, Postgres 9 auf Linux).

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, wie in diesem Blog-Eintrag hier beschrieben wurde, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt und auch der Pfad für das log-Dateien Verzeichnis in der Datei [server]/standalone/configuration/logging.properties.

Hier auch der WildFly 11 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Minimale Voraussetzung ist jeweils Java 8, er wird hier aber auch mit Java 9 betrieben. Der fertig konfigurierte WildFly 11 mit vielen weiteren Beispiel-Applikationen ist hier zum Download verfügbar, kann runtergeladen, entpackt und gestartet werden.

Auch hier ist nur der Pfad des JAVA_HOME anzupassen, damit dieser auf das tatsächliche JDK 9 – Verzeichnis des Server-Systems zeigt und ebenfalls der Pfad für das log-Dateien Verzeichnis in der logging.properties.

Mehr Informationen zum Java 9 Release gibt es hier:

https://www.technotification.com/2017/09/java-9-is-finally-released.html

Herunterladen kann man sich z. B. Java 9 für die gewünschte Betriebssystem-Plattform hier:

http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html

In Fällen mit Migrations-Bedarf gibt es hier weitere Informationen.

Hier die Release-Notes zu Java 9:

http://www.oracle.com/technetwork/java/javase/9-relnotes-3622618.html

Hier auch nochmal die ausführliche Dokumentation einer weiteren JEE Beispielapplikation mit Selenium-Tests und Javascript QUnit-Tests.

Hier das Beispiel-Projekte für die Eclipse-Entwicklungsumgebung JBoss Developer Studio: library-primefaces

Die Primefaces Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 11 Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server das library-primefaces.war einfach ins Server-Unterverzeichnis /standalone/deployments kopieren.

– Wenn das Deployment des library-primefaces.war Archivs nicht zeitnah startet, kann eine leere Textdatei namens library-primefaces.war.dodeploy gespeichert werden. Dann wird die Beispiel-Applikation sofort deployt und in wenigen Sekunden gestartet und kann aufgerufen werden:

http://[domainname z. B. localhost]:[http-port, z. B. 8080]/library-primefaces

Fazit: Wie man sieht, macht es einfach viel Freude, z.B. mittels JBoss Forge und JPA 2 Entities robuste, komfortable, Browser-basierte Primefaces 6-Fontends oder eben die responsiven Angular JS Apps auf dem Datenmodell zu erzeugen und weiterzuentwickeln und diese an einen neuen, die JEE7-Spezifikation erfüllenden JBoss WildFly (WildFly 9, WildFly 10 und 11) anzubinden. Die Blog-Einträge dazu können hier nach Stichworten und Erscheinungsmonat leicht durchsucht werden.

Somit besteht durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces 6, Angular JS und Bootstrap, sowie den die JEE7-Spezifikation implementierenden WebService-und Backend-Technologien folgen. Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test-Driven Development mit Java, sowie eine schöne Sommerzeit.

WebApps Migration JSF 2.2 MyFaces, RichFaces zu Primefaces, REST, JPA 2, JEE7 auf JBoss WildFly 10

Die Motivation

Ein oftmals relevantes Thema, wenn JEE Applikationen migriert werden sollen und die JSF-Applikationen gleich mit migriert werden, ist die Migration von RichFaces oder MyFaces-Implementierungen auf Primefaces. In diesem Blog-Eintrag hier soll dies anhand einer MyFaces JSF 2.2 Beispiel-Applikation bookstore auf die sehr vorteilhafte Primefaces-Technologie beschrieben werden. Die migrierte bookstore-primefaces ermöglicht dann die leichte Pflege von und die Suche nach Produkten bestimmter Kategorien und Bestellungen von Kunden, d. h. diese Beispielapplikation ermöglicht die Bearbeitung und Präsentation der Stammdaten (Kunden), Bestandsdaten (E-Books) und Bewegungsdaten (Abverkaufsdaten) einer Buchhandlung. Hatten die MyFaces-Beispiel-Applikationen noch extra Suchfelder und Schwächen bei der Sortierung der Ergebnislisten, bieten nach der Umstellung die Primefaces-Views das Durchsuchen, Sortieren und eine wählbare Pagination der Ergebnisliste bereits als Komfortfunktionen der robusten, aber stets elegant gestylten, etablierten, optimalen Primefaces Frontend-Technologie und deren Standard Primefaces-Funktionalitäten an.

Die Beispiel-Applikation bookstore-primefaces wird dann wird dann auf den JBoss WildFly 9 Final, WildFly 10 Final und WildFly 11 Final Servern deployt und kann hier getestet werden. Die Verwendung der JBoss WildFly JEE7-Application Server wurde in diesem Blog bereits einmal beschrieben. Dabei liefern die JEE6- und JEE7-Beispielapplikationen hier mit Hilfe eines EJB 3-getriebenen Backends (Session Beans als DAOs, „DAO“-Pattern) unter Anbindung der JPA 2 Entities („Table-Per-Class“-Pattern) mit Hilfe von DTOs über fachliche RESTful ServiceInterfaces oder per Microservices mit Docker, Spring Boot, siehe auch „ServiceBroker“-Pattern hier, die benötigten Daten zur Präsentation und Bearbeitung (Änderung, Löschen, Neuanlage) in den Web-Frontends, welche die WebServices konsumieren („ServiceConsumer“-Pattern).

Erstellung der Beispiel Applikation auf dem Datenmodell

Mittels DBeaver wird das Klassendiagramm der Entities aus den Datenbank-Objekten erstellt und aus diesen, nach dem Anlegen der Datenbank per Hibernate Envers während dem WildFly 10 Undertow Deployment mittels JBoss Forge Eclipse PlugIn im JBoss Developer Studio erst der REST-WebService mit den Endpoints generiert und danach die Primefaces 6 WebApp auf diesem RESTful WebService. Hier das E-R-Diagramm der Buchhandlungs-Datenbank:

E-R-diagram
(E-R-Diagramm der Beispiel-Datenbank „bookstore“ ohne die MySQL-Sequence-Tabellen.)

Dabei darf positiv erwähnt werden, dass man mittels JBoss Forge eine saubere, stringente Architektur in die JEE7-Applikation einbringt, die beim Weiterentwickeln der App sehr hilfreich ist und sowohl die Entwicklungszeit-Performance als auch die Codequalität deutlich steigert. Danke, JBoss Rockstars!

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

“TDD mit Java”

von Binaris Informatik erwähnt werden.

Es werden in der pom.xml die folgenden Maven Dependencies identifiziert, verwendet und importiert:

Maven JEE 7 JBoss-Artefakte und Maven Dependencies

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Dabei erfolgt die Aktivierung von CDI mittels beans.xml im WEB-INF Verzeichnis.

über die Architektur und die verwendeten Design Patterns der Beispiel-Applikationen gibt es bereits Informationen in diesem Blog-Eintrag hier und bereits vorausgegangenen Blog-Einträgen.

Einsatz von Primefaces

Die Einbindung der erforderlichen Primefaces-UI-Bibliothek primefaces-6.0.jar erfolgt ganz simpel über die entsprechende Maven Primefaces Dependency in der pom.xml. Es ist also kein extra /lib-Verzeichnis und keine build.xml für einen Ant Build-Prozess erforderlich, denn es wird einfach der folgende Eintrag zur pom.xml für den Maven Build hinzugefügt:

<dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>6.0</version>
    </dependency>

Allein durch Änderung dieses Eintrags in der pom.xml kann jederzeit auch auf die gewünschte Primefaces-Version umgestellt werden. Bei der Umstellung der ca. 80 XHTML-Views von MyFaces auf Primefaces sind der Primefaces Showcase und der Primefaces Users Guide (PDF) sehr hilfreich. Der größte Aufwand der Migration besteht hierbei im Austausch der Standard MyFaces-Tags (h: Präfix) durch die Primefaces-Tags (p: Präfix) ist also für die simplen Beispiel-Applikationen leicht umsetzbar. Ebenfalls wurde das sehr gute bei DZone erhältliche PDF zur JSTL (Java Server Pages Template Library) verwendet und zwei weitere Dokumente über genauere Details zum JSF 2-Lifecycle, die sich hier und hier zum Download finden.

Aktiviert werden die Primefaces-Tags in der gewünschten XHTML-Seite mittels folgendem Eintrag im Dokumenten-Kopf der XHTML-View xmlns:p=“http://primefaces.org/ui und die zusätzliche Funktionalität der JSTL-Tags wird entsprechend, wie in den MyFaces-Views auch, per xmlns:c=“http://java.sun.com/jsp/jstl/core verfügbar gemacht.

Durch die Verwendung von CDI 1.1 haben die JSF ManagedBeans stattdessen die Annotation @Named und bleiben, bis auf ein paar Helper-Methoden zur Ausgabeformatierung, nahezu identisch zu den ManagedBeans der MyFaces-Applikation bookstore.

Weitere Informationen zu JSF 2.2, CDI und dem @ViewScope gibt es auch auf stackoverflow.com. Die dort beschriebenen Themen finden sich auch in den ManagedBeans der JSF 2.2 Beispiel-Applikation wieder.

Für die Migration von RichFaces 4 Applikationen auf Primefaces gibt es in diesem Blog hier ebenfalls eine sehr nützliche Anleitung. Es wird hierzu auch noch einen Blog-Eintrag geben. Hier noch eine weitere SPA (Single Page Appliction) Beispiel-Applikation, bei der das Testen des Backends mittels Arquillian und das integrative Testen des Frontends mittels Selenium (Workflow und UX Elemente) und QUnit (Javascript-Framework zum Test-Aufruf der REST-Endpoints aus einer Browser-View) ausführlich beschrieben ist.

Weitere Informationen zu JSF 2.2, CDI und Primefaces finden sich auch hier im Blog von Balus C. Detail-Informationen zur JavaScript-Bibliothek jQuery, die vom Primefaces-Framework sehr intensiv verwendet wird, finden sich hier: jquery.org

forge.taglib.xml:

Die in den Beispiel-Applikationen verwendete, moderne Forge TagLib kann sehr einfach erweitert werden, indem die zusätzlich benötigten Methoden in der ViewUtils-Klasse ergänzt werden und deren Signaturen in der forge.taglib.xml im Verzeichnis src/main/webapp/WEB-INF/classes/META-INF/ bekanntgemacht werden, also genau wie bei klassischen TagLibs mit dem Unterschied, dass die verwendeten Tags innerhalb der Applikation in der ViewUtils-Klasse implementiert werden, was für die Applikation zusätzliche Sicherheit bringt. Diese Tags sind für jede Applikation sehr leicht adaptierbar und können genau auf die Applikation zugeschnitten werden. Die TagLib wird in der entsprechenden .xhtml-View über den Eintrag xmlns:forgeview=“http://jboss.org/forge/view“ mit dem Prefix forgeview unter der DOCTYPE-Definition im ui:composition eingebunden und mittels forgeview-Prefix per Expression Language (EL) an der gewünschten Stelle mit den richtigen Input-Parametern aufgerufen. Hier die forge.taglib.xml für die Beispiel-Applikation bookstore-primefaces:

<?xml version=“1.0“ encoding=“UTF-8“?>
<!DOCTYPE facelet-taglib PUBLIC “-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN“ “http://java.sun.com/dtd/facelet-taglib_1_0.dtd“>
<facelet-taglib xmlns=“http://java.sun.com/JSF/Facelet“>
<namespace>http://jboss.org/forge/view</namespace>
<function>
<function-name>asList</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.util.List asList(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>display</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.lang.String display(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>count</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
int count(java.util.Collection)
</function-signature>
</function>

<function>
<function-name>displayShort</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayShort(java.lang.Object, int)
</function-signature>
</function>

<function>
<function-name>displayRange</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayRange(java.lang.Object,java.lang.Object)
</function-signature>
</function>

<function>
<function-name>displayFirst</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.lang.String displayFirst(java.lang.Object)
</function-signature>
</function>

<function>
<function-name>listExcludingSelectedOne</function-name>
<function-class>de.binaris.bookstore.view.ViewUtils</function-class>
<function-signature>
java.util.List listExcludingSelectedOne(java.util.Collection,java.lang.Object)
</function-signature>
</function> 
</facelet-taglib>

Mit entsprechenden Implementierungen der Tag-Funktionalitäten in der ViewUtils-Klasse unter Verwendung generischer Typen T und Collections List<T>.

Durch die soeben gezeigten Beispiele ist erkennbar, wie simpel es ist, eigene Tags für die .xhtml-Views heutiger JSF 2-Applikationen zu definieren und deren Utility functions-Signaturen im WEB-INF/classes/META-INF-Verzeichnis des classpaths als *name*-taglib.xml mit dem genannten XML Document-Header und dem jboss-forge-view namespace zu deklarieren (s.o. forge.taglib.xml).

Alle XML und XHTML-Dateien der Primefaces-Beispielapplikation finden sich im Deployment-Archiv bookstore-primefaces.war im /target-Unterverzeichnissen des Projekts bookstore-primefaces.

WildFly Anbindung an MySQL5, Postgres 9 und deklarative WildFly 11 Hibernate-Konfiguration

Die Konfiguration des JBoss WildFly Application Servers 11 Final erfolgt analog der Konfigurationen der JBoss Application Server WildFly 8.1, 8.2, 9 und 10 Final und betrifft die Datasource, die MySQL-Datenbank Inno DB 5.x, Hibernate 5, das Logging und den Connection Pool und ist, genau wie die Test-Frameworks und die Aktivierung der RESTful WebService-Schnittstelle ein wichtiges Thema, welches noch genauer beschrieben werden wird. Die Anbindung an die relationale Datenbank MySQL 5 erfolgt übrigens analog für die JBoss Application Server WildFly 8.1, 8.2, 9, 10 und WildFly 11.

Je nach verwendeter Datenbank kann in der persistence.xml im Deployment-Paket der Beispiel-App bookstore-primefaces ein entsprechender Dialekt eingetragen werden:

Hier die /META-INF/persistence.xml der Beispiel-Applikation für MySQL5:

<xml version=“1.0″ encoding=“UTF-8″?>
    <persistence version=“2.0″ xmlns=“http://java.sun.com/xml/ns/persistence“  xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
    xsi:schemaLocation=“http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd“>
    <persistence-unit name=“BookstorePU“>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>
            java:jboss/datasources/BookstoreDatasource
    </jta-data-source>
    <properties>
    <property name=“hibernate.hbm2ddl.auto“ value=“none“/>
    <property name=“hibernate.show_sql“ value=“false“/>
    </properties>
    </persistence-unit>
    </persistence>

Dafür wird je Datenbank die folgende Datasource benötigt und ist in der standalone/configuration/standalone.xml des JBoss WildFly Servers unter den <datasources> einzutragen:

Für MySQL 5:

    <datasource jndi-name=“java:jboss/datasources/BookstoreDatasource“ pool-name=“BookstoreDS“
    enabled=“true“>
    <connection-url>jdbc:mysql://localhost:3306/bookstore</connection-url>
    <driver>mysql-connector-java-5.1.34.jar</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
    <pool>
    <min-pool-size>10</min-pool-size>
    <max-pool-size>100</max-pool-size>
    <prefill>true</prefill>
    </pool>
    <security>
    <user-name>root</user-name>
    <password>das entsprechende Passwort</password>
    </security>
    <statement>
    <prepared-statement-cache-size>32</prepared-statement-cache-size>
    <share-prepared-statements>true</share-prepared-statements>
    </statement>
    </datasource>

Für Postgres 9:

    <datasource jndi-name=“java:jboss/datasources/BookstoreDatasource“ pool-name=“BookstoreDS“ enabled=“true“>
    <connection-url>jdbc:postgresql://localhost:5432/bookstore</connection-url>
    <driver>postgresql-9.4.1209.jar</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED
    </transaction-isolation>
    <pool>
    <min-pool-size>10</min-pool-size>
    <max-pool-size>100</max-pool-size>
    <prefill>true</prefill>
    </pool>
    <security>
    <user-name>root</user-name>
    <password>[das entsprechende Password]</password>
    </security>
    <statement>
    <prepared-statement-cache-size>32</prepared-statement-cache-size>
    <share-prepared-statements>true</share-prepared-statements>
    </statement>
    </datasource>

Für die erfolgreiche Anbindung der soeben deklarierten Datasoures werden die Datenbank-Treiber auf Modul-Ebene des JBoss WildFly konfiguriert, wie hier für den MySQL JDBC4 Treiber beispielhaft auf dem WildFly 8.2 beschrieben wurde.

Damit die Datasources die soeben konfigurierten Treiber auch finden, werden diese in der /standalone/configuration/standalone.xml bei den <datasources> deklariert:

Für Postgres 9:

<driver name=“postgresql-java-9.4.1209.jar“ module=“org.postgresql“>
   <driver-class>org.postgresql.Driver</driver-class>
   <xa-datasource-class>org.postgresql.xa.PGXADataSource
   </xa-datasource-class>
</driver>

Für MySQL 5:

<driver name=“mysql-connector-java-5.1.34.jar“ module=“com.mysql“>
   <driver-class>com.mysql.jdbc.Driver</driver-class>
   <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
   </xa-datasource-class>
</driver>

Hier auch der komplette MySQL-Export der Beispiel-Applikation bookstore-primefaces:

Für MySQL 5: export_bookstore_database.sql

Die Hibernate-Konfiguration des WildFly 11 Final Servers ist ebenfalls leicht durchführbar. Hierfür werden im WildFly11 Server-Verzeichnis wildfly-11.0.0.Final/modules/system/layers/base/org/hibernate/main folgende Dateien hinterlegt:

– hibernate-core-5.1.10.Final.jar
– hibernate-entitymanager-5.1.10.Final.jar
– hibernate-envers-5.1.10.Final.jar
– hibernate-java8-5.1.10.Final.jar
– jipijapa-hibernate5-11.0.0.Final.jar
– module.xml
– mysql-connector-java-5.1.34.jar
– mysql-connector-java-5.1.34.jar.index

Die fertige Beispiel-Applikation bookstore-primefaces kann zusammen mit den anderen Beispiel-Applikationen hier betrachtet werden (MySQL5 Server, Postgres 9 auf Linux).

Hier der WildFly 10 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Voraussetzung ist jeweils Java 8.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann runtergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, wie in diesem Blog-Eintrag hier beschrieben wurde, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt und auch der Pfad für das log-Dateien Verzeichnis in der Datei [server]/standalone/configuration/logging.properties.

Hier auch der WildFly 11 Final als Zip-Archiv zum Download und Entpacken: Für Linux hier und für Windows hier. Minimale Voraussetzung ist jeweils Java 8, er wird hier aber bereits mit Java 9 betrieben. Der fertig konfigurierte WildFly 11 mit vielen weiteren Beispiel-Applikationen ist hier zum Download verfügbar, kann runtergeladen, entpackt und gestartet werden.

Auch hier ist nur der Pfad des JAVA_HOME anzupassen, damit dieser auf das tatsächliche JDK 9 – Verzeichnis des Server-Systems zeigt und ebenfalls der Pfad für das log-Dateien Verzeichnis in der logging.properties.

Mehr Informationen zum neulich erschienenen Java 9 Release gibt es hier:

https://www.technotification.com/2017/09/java-9-is-finally-released.html

Herunterladen kann man sich das neue Java 9 für die gewünschte Betriebssystem-Plattform hier:

http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html

In Fällen mit Migrations-Bedarf gibt es hier weitere Informationen.

Hier die Release-Notes zu Java 9:

http://www.oracle.com/technetwork/java/javase/9-relnotes-3622618.html

Hier auch nochmal die ausführliche Dokumentation einer weiteren JEE Beispielapplikation mit Selenium-Tests und Javascript QUnit-Tests.

Hier das Beispiel-Projekte für die Eclipse-Entwicklungsumgebung JBoss Developer Studio: bookstore-primefaces

Die Primefaces Beispiel-Applikation dieses Blog-Eintrags kann danach selbst erneut deployt werden, ohne den bereits gestarteten WildFly 11 Application Server überhaupt stoppen oder erneut starten zu müssen. Bei laufendem Server einfach ins Server-Unterverzeichnis /standalone/deployments deployen

– Wenn das Deployment des bookstore-primefaces.war Archivs nicht unverzüglich startet, eine leere Textdatei namens bookstore-primefaces.war.dodeploy speichen und
sofort wird die Beispiel-Applikation deployt und in wenigen Sekunden gestartet und kann aufgerufen werden:

http://[domainname z. B. localhost]:[http-port, z. B. 8080]/bookstore-primefaces

Fazit: Wie man sieht, macht es einfach viel Freude, z.B. mittels JBoss Forge und JPA 2 Entities robuste, komfortable, Browser-basierte Primefaces 6-Fontends oder eben die responsiven Angular JS Apps auf dem Datenmodell zu erzeugen und weiterzuentwickeln und diese an einen neuen, die JEE7-Spezifikation erfüllenden JBoss WildFly (WildFly 9, WildFly 10 und 11) anzubinden. Die Blog-Einträge dazu können hier nach Stichworten und Erscheinungsmonat leicht durchsucht werden.

Somit besteht durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Webframeworks wie Primefaces 6, Angular JS und Bootstrap, sowie den die JEE7-Spezifikation implementierenden WebService-und Backend-Technologien folgen. Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels Scrum und dem Test-Driven Development mit Java, sowie weiterhin schöne Sommerferien.

Services – mit REST, JPA 2, EJB 3.2, JEE7, WildFly, Forge

 Die Motivation

Das MVC Framework ‘Angular JS‘ ermöglicht ebenfalls unter Einsatz des Router Design Patterns und des Front Controller Design Patterns das Erstellen von responsiven (Mobile) Applikationen zur Stammdatenpflege von über ein Service Interface verfügbaren Daten aus einem RESTful WebService. Die erstellte JEE7 Beispiel-Applikation ‘bookstore-angularjs‘ arbeitet auf Basis der Template-Technologie und ermöglicht die leichte Pflege von und die Suche nach Produkten, z. B. Bücher, bestimmter Kategorien, z. B. Belletristik, und Bestellungen von Kunden etc. Die Beispiel-Applikation verwendet JPA-Entities, außerdem werden mittels JBoss Forge sowohl JSF 2.2 Benutzerschnittstellen verfügbar gemacht, als auch die Angular JS GUIs. An diesem einfachen, übersichtlichen JEE7 Beispielprojekt wird dadurch die Verwendung der eingesetzten Technologien gezeigt und dieses als .war Archiv-Deployment auf dem JBoss WildFly 8.2 Application Server und weiteren Application Servern zum Test zur Verfügung gestellt. Hier das Entity-Relationship Diagramm (“E-R-Diagramm“) der ‘bookstore‘ MySQL-Datenbank:

E-R-diagram
(E-R-Diagramm der Beispiel-Applikation ‘bookstore-angularjs’)

TDD der Beispiel Applikation

Wie zusätzliche Selenium-Tests mit dem Selenium FireFox-PlugIn durchgeführt und diese Tests gespeichert werden können, wurde bereits in einem Blog-Eintrag in diesem Blog beschrieben. Diese Selenium Tests können gespeichert und in einer Test-Suite zusammengefasst werden, um auch später jederzeit die korrekte Funktionsweise der Eingabe-Aktionen und Link-Aufrufe der responsiven Applikation automatisiert verifizieren zu können.

Die Technologien:

Die Beispiel-Applikation ‘bookstore-angularjs‘ verwendet im Frontend Angular.js und HTML5-/CSS3, als auch die JavaScript-Bibliotheken Bootstrap.js, Backbone.js, jQuery und jQuery Mobile, sowie die Underscore.js Tools.

Für das JAX-RS Service-Backend kommt Java EE zum Einsatz (Stateful/Stateless EJB 3.2 und JEE 7) und im Backend Model JPA 2.1 Entities. Die Applikation wurde auf dem ‘JBoss WildFly 8.2‘ und dem ‘JBoss EAP 6.3‘ Applikationsserver, sowie weiteren Applikationsservern erfolgreich deployt und getestet und kann gerne weiterverwendet und als Open Source weiterentwickelt werden.

Für die Weiterentwicklung, den Build und das Deployment des Projekts ist mindestens Java 6 und Maven 2 erforderlich. Als Entwicklungsumgebung wurde Eclipse in Form des “JBoss Developer Studios“ mit Java 7 und Java 8 verwendet und die FireFox WebDeveloper IDE/Tools.

Wie für die Applikation ‘bookstore-angularjs‘ in der pom.xml des Beispielprojekts nach erfolgtem Download erkennbar ist, werden auch hier die folgenden JEE 7 Artefakte für den JBoss WildFly verwendet und importiert:

WildFly JBoss Java EE 7 Specification APIs with Tools:
– jboss-javaee-7.0-with-tools
WildFly JBoss Java EE 7 Specification APIs with Resteasy:
– jboss-javaee-7.0-with-resteasy
WildFly JBoss Java EE 7 Specification APIs with Hibernate:
– jboss-javaee-7.0-with-hibernate

Weiterhin werden die folgenden JEE Dependencies (für das Servlet API, Annotationen, JAX-RS Implementierungen, JBoss RESTEasy, Jackson, Hibernate, JPA und EJB) verwendet:

– jboss-annotations-api_1.1_spec
– jboss-jaxrs-api_2.0_spec
– resteasy-jackson2-provider

– hibernate-jpa-2.1-api
– jboss-ejb-api_3.2_spec
– hibernate-jpamodelgen
– jboss-servlet-api_3.1_spec

Einsatz von CDI

Und ebenfalls die CDI 1.1 Dependency:

– cdi-api_1.1

Die Aktivierung von CDI erfolgt mittels beans.xml im WEB-INF Verzeichnis.

Die Architektur

a) Das Service Backend:

Die Beispiel-Applikation verwendet die folgenden Entities, die hier gelistet sind:

– Address
– Country
– Category
– Customer
– Product
– Item
– OrderLine
– PurchaseOrder
– CreditCard
– CreditCardType

Die Relationen und Constraints (HibernateValidation) zeigen sich auch in den entsprechenden JPA 2.1 Entities und deren Annotationen.

b) Der RESTful WebService mit den Service-Endpoints:

Soweit die beschriebenen Entities der Beispiel-Applikation ‘bookstore-angularjs‘. Hieraus lassen sich mit dem JBoss Developer Studio und dem JBoss Forge PlugIn über das Kontextmenü auf dem Maven-Projekt der RESTful WebService mit den Service-Endpoints generieren.

Die RESTful WebService-Endpoints verwenden wiederum die folgenden DTOs.

– AddressDTO
– CategoryDTO
– CountryDTO
– CreditCardDTO
– CustomerDTO
– ItemDTO
– NestedCategoryDTO
– NestedCountryDTO
– NestedCustomerDTO
– NestedItemDTO
– NestedOrderLineDTO
– NestedProductDTO
– NestedPurchaseOrderDTO
– OrderLineDTO
– ProductDTO
– PurchaseOrderDTO

Nun könnten auf dem Maven-Projekt mittels Forge/Scaffold die gewünschten View-Beans für eine JSF-Beispiel-Applikation generiert werden. Die Architektur und die Assoziationen für die Auswahl des Angular JS MVC Frameworks wurden bereits im Beispiel-Projekt ‘educationorganizer‘ eingehend erläutert und können dort nachgelesen werden. Für JSF 2.2 Views (Auswahl JSF) wüde man die folgenden BackingBeans mit den entsprechenden CDI Annotationen (@Named anstatt @ManagedBean, etc.) erhalten:

– CategoryBean
– CountryBean
– CustomerBean
– ItemBean
– LocaleBean
– OrderLineBean
– ProductBean
– PurchaseOrderBean

Die Konfiguration des JBoss WildFly Application Servers kann hier nachgelesen werden.

Diese Minimal-Konfiguration betrifft:

– die MySQL Datenbank/den Java MySQL-Datenbank-Treiber und die MySQL-Datasource der
‘bookstore-angularjs‘ Beispiel-Applikation
– die Hibernate Version und ihre interessierenden Features/Dependencies
– das Start-Skript des WildFly Application Servers

Eine fertige Version mit Beispiel-Konfiguration der Beispiel-Applikation ‘bookstore-angulars‘ kann aus dem /target-Unterverzeichnis des heruntergeladenen Beispielprojekts hier, in ein beliebiges Verzeichnis ausgepackt und durch Starten von standalone.bat (Windows) oder standalone.sh (Linux) sofort gestartet werden. Anschließend ist die Beispiel-Applikation aufrufbar unter der folgenden Url:

http://localhost:8080/bookstore-angularjs/app.html#/ oder hier bei den JEE-Beispiel-Applikationen.

Die Test-Frameworks und die Tests

a) Arquillian Tests für das Service-Backend

wurden in einem Blog-Eintrag in diesem Blog bereits erklärt.

b) QUnit-Tests für die REST-Services

wurden in einem Blog-Eintrag in diesem Blog bereits erklärt.

c) Selenium Tests für die Benutzerschnittstelle

wurden in einem Blog-Eintrag in diesem Blog bereits erklärt.

Bei dieser Gelegenheit darf auch das sehr effektive, interessante und erfolgreiche Seminar

TDD mit Java”

von Binaris Informatik erwähnt werden.

Wie Test-Suites, die mit dem Selenium IDE PlugIn durchgeführt wurden und automatisiert ausgeführt werden können, erstellt werden, wurde bereits in einem Blog Eintrag in diesem Blog beschrieben.

Hier das ‘bookstore-angularjs‘ Eclipse-Projekt zum Download:

https://github.com/UweKrull/repository/raw/master/bookstore-angularjs.zip

Hier das SQL-Skript zum Anlegen der MySQL-Datenbank zum Download:

http://educationorganizer.de/mysql_export_bookstore_database_sql.txt

Hier der WildFly 8.2 als Zip-Archiv zum Download und Entpacken:

http://download.jboss.org/wildfly/8.2.0.Final/wildfly-8.2.0.Final.zip

Installation/Start der Beispiel-Applikation:

– Die MySQL-Datenbank per SQL-Skript anlegen.

– JAVA_HOME, JBOSS_HOME entsprechend setzen und

in der standalone.bat oder standalone.sh verwenden und nur noch die folgende datasource in der standalone/configuration/standalone.xml eintragen:

<datasource jndi-name=“java:jboss/datasources/BookstoreDatasource“ pool-name=“BookstoreDS“ 
        enabled=“true“>
        <connection-url>jdbc:mysql://localhost:3306/bookstore</connection-url>
        <driver>mysql-connector-java-5.1.34.jar</driver>
        <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
        <pool>
                <min-pool-size>10</min-pool-size>
                <max-pool-size>100</max-pool-size>
                <prefill>true</prefill>
        </pool>
        <security>
                <user-name>root</user-name>
                <password>das entsprechende Passwort</password>
        </security>
        <statement>
                <prepared-statement-cache-size>32</prepared-statement-cache-size>
                <share-prepared-statements>true</share-prepared-statements>
        </statement>
</datasource>

– bookstore-angularjs.war ins Server-Unterverzeichnis /standalone/deployments speichern

– Neben bookstore-angularjs.war eine leere Textdatei namens bookstore-angularjs.war.dodeploy speichen.

– Server starten per standalone.bat oder standalone.sh, http://localhost:8080/bookstore-angularjs/ aufrufen

– Die Selenium Tests per Selenium IDE PlugIn/Recorder erstellen und ausführen.

Die fertig konfigurierte Version des WildFly 10.Final mit vielen Beispiel-Applikationen der vorigen Blog-Einträge hier zum Download bereit kann heruntergeladen, entpackt und gestartet werden. Dabei ist nur der Pfad für JAVA_HOME in der standalone.xml anzupassen, damit dieser auf das tatsächliche JDK 8-Verzeichnis des Server-Systems zeigt.

Allen interessierten Leserinnen und Lesern weiterhin viel Freude bei der agilen Softwareentwicklung mittels  Scrum und dem Test Driven Development mit Java, sowie schöne Sommerferien und einen erholsamen Urlaub.