WebApps – Migration auf JSF 2.2 Primefaces, REST, JPA 2, JEE7 auf JBoss WildFly 10

Die Motivation

Migrationen von RichFaces oder MyFaces-Implementierungen auf Primefaces sind immer ein relevantes Thema, wenn von JBoss AS 7 auf WildFly migriert werden soll und die JSF-Applikationen gleich mit migriert werden sollen, weshalb in diesem Blog-Eintrag hier die Migration von einer MyFaces JSF 2.2 Beispiel-Applikation greetingcardsadmin  auf die sehr vorteilhafte Primefaces-Technologie beschrieben werden soll.

Die mittels JBoss Forge Eclipse PlugIn erstellte Applikation greetingcardsadmin wird zur Primefaces-Applikation greetingcardsadmin-primefaces migriert und unterstützt den Einsatz der robusten, aber stets elegant gestylten, etablierten, optimalen Primefaces Frontend-Technologie.

Die Beispiel-Applikation greetingcardsadmin-primefaces wird dann auf dem JBoss WildFly 10 Final deployt. Dabei ist die Verwendung der JBoss WildFly JEE7-Application Server 9 und 10 in diesem Blog  bereits beschrieben worden. 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 Standard Primefaces-Funktionalitäten an.

Migration der Beispiel Applikation auf Primefaces

Wie die Applikationen greetingcardsadmin mittels JBoss Forge erstellt werden kann, ist bereits in diesem Blog beschrieben worden, und wie in der pom.xml des jeweiligen Beispielprojekts nach erfolgtem Download erkennbar ist, werden auch hier in der Applikation greetingcardsadmin-primefaces 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

Dabei erfolgt die Aktivierung von CDI 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.

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 greetingcardsadmin. 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.

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 greetingcardsadmin-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. greetingcardsadmin.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. greetingcardsadmin.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. greetingcardsadmin.view.ViewUtils</function-class>
               <function-signature>
                              int count(java.util.Collection)
               </function-signature>
   </function>

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

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

ViewUtils:

package de.binaris. greetingcardsadmin.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 mehrere Blog-Einträge 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  greetingcardsadmin.war im  /target-Unterverzeichnissen des Projekts greetingcardsadmin-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 ebenfalls erwähnt werden.

Die Primefaces Beispiel-Applikation arbeitet über die RESTful WebService-Schnittstelle auf derselben Datenbanken, wie die MyFaces-Applikation greetingcardsadmin, weshalb für die Primefaces Beispiel-Applikationen das selbe mit HeidiSQL erstellte Datenbank-Skripte verwendet werden kann.

Die fertig migrierte Version der Beispiel-Applikation greetingcardsadmin-primefaces kann  hier (WildFly) betrachtet werden.

Hier das migrierte Beispiel-Projekte für die Entwicklungsumgebung JBoss Developer Studio:
greetingcardsadmin-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 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 das Archiv greetingcardsadmin-primefaces.war aus dem greetingcardsadmin/target-Verzeichnis ins Server-Unterverzeichnis /standalone/deployments speichern

Sofort wird die Beispiel-Applikationen deployt und in wenigen Sekunden gestartet oder kann hier aufgerufen werden.

Wie man sieht, macht es viel Freude, die XHTML-Views der beschriebenen Beispiel-Applikation von MyFaces oder RichFaces auf die vorteilhafte Primefaces-Technologie umzustellen. Es besteht also durchaus die Möglichkeit, dass noch weitere Blog-Einträge zu Primefaces oder RichFaces und den die JEE7-Spezifikation implementierenden Technologien, sowie AngularJS Applikationen folgen.

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