Architekturen – mit Spring CDI, JPA, JBoss, JavaEE

Montag, 30. Juni 2014 von  
unter Fachartikel Architektur

 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, produktiv verwendet und erfolgreich einsetzt, macht weiterhin Spring und wer bereits Java EE kann, produktiv verwendet und erfolgreich einsetzt, macht weiterhin Java EE, z. B. mit dem sehr empfehlenswerten JBoss Application Server.

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 korrekte 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 Technologie 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 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 einer JAR-Datei

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 Funktion

Java Persistence API /META-INF/persistence.xml WAR benötigt eigene Config-Datei.
Enterprise Java Beans WAR /META-INF/ejb-jar.xml Nein
Java Server Faces /META-INF/faces-config.xml Nein
Contexts & Dependency Injection /META-INF/beans.xml Ja
Web Configuration /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, worüber es hier bereits einen Blog-Eintrag gibt), 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. Über das Unternehmens-Login mittels JBoss Seam gibt es hier bereits einen Blog-Eintrag. In Spring wird auch gerne das Spring Security Framework verwendet, worüber es hier bereits einen Blog-Eintrag gibt.

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 ended 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. Die Consumer Bean in, die 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 weiss 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. Hierüber gibt es bereits einen Blog-Eintrag in diesem Blog hier.

In CDI weiss 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:

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“);
}

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“));
}

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

  1. 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.
  2. 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 Context, oder in JSF UIViewRoot an den @ViewScoped Context 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 der Persistence Version 1.0). Und hier eine persistence.xml der Version 2.0:

<?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 einen Blog Eintrag hier.

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 ein paar Blog-Einträge in diesem Blog hier, hier, und hier. Weitere Blog-Beiträge sind hier unter der Rubrik BEITRÄGE zu finden.

Doch nun zu einem EJB-Beispiel:

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 im Unter-Verzeichnis src/main/org/jboss/seam/persistence des Seam Zip-Archivs. Weitere interessante Beispiel-Projekte finden sich z. B. auch hier (http://ocpsoft.org/wp-content/uploads/). Zum Thema „Services – mit Spring und JPA Implementierungen“ und zum PersistenceContext gibt es bereits hier einen Blog-Eintrag in diesem Blog.

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 nicht-gespeicherten Ä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 EntityManger/Session) 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/Session) 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.

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 der @ConversationScoped und der @RequestScoped (oder 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, worüber es hier bereits einen Blog-Eintrag in diesem Blog gibt. Darüber hinaus kann auch jederzeit Hibernate (von RedHat) innerhalb der robusten Java EE Seam Faces Applikationen verwendet werden oder mit anderenGUI/Javascript-generierenden Frameworks, wie Vaadin, worüber es hier einen Blog-Eintrag gibt, oder mit GWT. Deklarative @Transactional(BEGIN) und @Transactional(END) annotierte Methoden können ebenfalls verwendet werden wie UserTransactons, womit man alle Möglichkeiten hat. Zusätzlich können weitere CDI Erweiterungen verwendet werden und Interceptoren (ähnlich wie bei Spring AOP).

EJBs wrappen erzeugte 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 die /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.

    Hier noch der Hinweis auf einen sehr guten Blog-Eintrag zu dem Thema von RedHat/ocpsoft hier. Bei DZone gibt es ebenfalls viele interessante Diskussionen und Beispiele sowie Vergleiche dazu. Zum Thema “TDD mit TestNG“ gibt es hier in diesem Blog auch einen Blog-Eintrag.

    Hier auch der Hinweis auf das

    sehr interessante, sehr empfehlenswerten Seminar “TDD mit Java“ von Binaris

    hier und hier.

    Allen interessierten Leserinnen und Lesern noch eine schöne Sommerzeit mit viel Freude bei der WM.

Kommentare

29 Kommentare zu “Architekturen – mit Spring CDI, JPA, JBoss, JavaEE”

  1. Von Services – TDD mit Arquillian, JSF2, JEE6, CDI : binaris informatik GmbH am Donnerstag, 30. April 2015 00:00

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im /WEB-INF […]

  2. Von Services – mit Angular JS, REST, JPA 2, JEE7, WildFly : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Mittwoch, 20. Mai 2015 20:16

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im /WEB-INF […]

  3. Von Services – mit REST, JPA 2, EJB 3.2, JSF 2.2, Angular JS : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Dienstag, 30. Juni 2015 19:12

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im /WEB-INF […]

  4. Von Services – mit REST, JPA 2, EJB 3.2, JSF 2.2, Angular JS, Bootstrap, JBoss Forge : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Mittwoch, 30. September 2015 19:15

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  5. Von Services – mit REST, JPA 2, EJB 3.2, WebFrameworks, JBoss WildFly : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Samstag, 31. Oktober 2015 20:51

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  6. Von Services – mit Angular JS, REST, JPA 2, JEE7 auf JBoss WildFly 9 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 22. November 2015 19:18

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  7. Von Services – TDD mit Selenium, QUnit JS, JEE6 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Donnerstag, 10. Dezember 2015 09:21

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im /WEB-INF […]

  8. Von WebApps – mit REST, JPA 2, JSF 2, AngularJS, JEE6, JEE7 auf JBoss WildFly 9 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 13. Dezember 2015 22:01

    […] verwenden unter der Haube CDI, worüber es bereits Blog-Einträge in diesem Blog gibt hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  9. Von WebApps – mit REST, JPA 2, EJB 3.2, Angular JS, JSF 2.2 auf JBoss WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 31. Januar 2016 11:59

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  10. Von WebApps – mit JSF 2.2, Primefaces 5, REST, JPA 2, JEE7, JBoss WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 28. Februar 2016 19:46

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  11. Von WebApps – Migration JSF 2.2 MyFaces, RichFaces zu Primefaces, REST, JPA 2, JEE7 auf JBoss WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 27. März 2016 19:09

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  12. Von WebApps – Migration mit JSF 2.2 Primefaces, REST, JPA 2, JEE7 auf JBoss WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 1. Mai 2016 12:37

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  13. Von WebApps – vom Modell zur App, mit JSF 2.2, Angular JS, REST, JPA 2, JBoss WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Montag, 30. Mai 2016 21:52

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  14. Von WebApps – Responsiv mit Angular JS, REST, JEE7, Java 8 auf JBoss WildFly 9 und 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Freitag, 1. Juli 2016 00:00

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  15. Von WebApps – Primefaces 6, REST, JEE7, Angular JS auf JBoss WildFly 9 und 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 24. Juli 2016 09:03

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  16. Von WebApps – mit Angular JS, REST, JEE7, JBoss Forge, Java 8, MySQL 5, Postgres 9, MSSQL Server 2016 auf JBoss WildFly 9 und 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Samstag, 20. August 2016 00:10

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  17. Von WebApps – RESTful mit JSF 2, Primefaces 6, JPA 2, EJB 3, JEE7, Java 8 auf JBoss WildFly : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Sonntag, 27. November 2016 18:51

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  18. Von WebApps – mit Angular JS, JQuery, REST, JEE7, Java 8, MySQL 5, Postgres 9, auf JBoss WildFly : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Freitag, 13. Januar 2017 22:03

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  19. Von WebApps – mit JPA 2, JSF 2.2, Primefaces 6, REST, JEE7, Java 8 auf JBoss WildFly 9 und WildFly 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Dienstag, 24. Januar 2017 21:15

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  20. Von WebApps – Generativ mit Angular JS, REST, JEE7, Java 8 auf JBoss WildFly 9 und 10 : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Dienstag, 24. Januar 2017 21:16

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  21. Von WebApps – JSF 2, RichFaces 4, Primefaces 6, REST, JPA 2, EJB 3, JEE7, JBoss WildFly : Softwareentwicklung, Projektmanagement & Schulung | binaris informatik GmbH am Montag, 6. Februar 2017 11:55

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  22. Von WebApps – Primefaces 6, FileUploads, JSF 2, JPA 2, EJB 3, JEE7, REST, AngularJS, WildFly : Karriere als Software Entwickler | binaris informatik GmbH am Mittwoch, 22. Februar 2017 19:39

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  23. Von WebApps – JSF 2, Primefaces, FileDownloads, JPA 2, EJB 3, JEE7, REST, MySQL, WildFly : Karriere als Software Entwickler | binaris informatik GmbH am Sonntag, 26. März 2017 20:01

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  24. Von WebApps – Primefaces 6, JPA 2, EJB 3, PDFs parsen, iText, MySQL, REST, JEE7, WildFly : Karriere als Software Entwickler | binaris informatik GmbH am Sonntag, 23. April 2017 15:48

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  25. Von WebApps – mit Angular JS, JQuery, REST, JEE7, JBoss Forge, Java 8, MySQL 5, auf JBoss WildFly 9 und 10 : Karriere als Software Entwickler | binaris informatik GmbH am Sonntag, 28. Mai 2017 16:37

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  26. Von WebApps – Wizards mit Primefaces 6, JSF 2, JPA 2,EJB 3, JEE7, Postgres 9, REST, WildFly 10 : Karriere als Software Entwickler | binaris informatik GmbH am Donnerstag, 29. Juni 2017 17:04

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  27. Von WebApps – mit Primefaces 6, JSF2,JPA2,EJB 3, JEE7, Java 8, REST auf WildFly 9, 10 und 11 : Karriere als Software Entwickler | binaris informatik GmbH am Samstag, 30. September 2017 19:20

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  28. Von WebApps – RESTful mit JSF 2, JPA 2, EJB 3, Primefaces 6, JEE7, Java 8 und 9 auf WildFly 9, 10 und 11 : Karriere als Software Entwickler | binaris informatik GmbH am Mittwoch, 1. November 2017 13:02

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

  29. Von WebApps – mit Angular JS, JQuery, REST, JEE7, JPA 2, Java 8 und 9, MySQL 5 auf JBoss WildFly 9, 10 und 11 : Karriere als Software Entwickler | binaris informatik GmbH am Sonntag, 26. November 2017 14:58

    […] CDI und JavaEE gibt es bereits Blog-Einträge in diesem Blog hier und hier. Die Aktivierung von CDI erfolgt, wie beschrieben, mittels beans.xml im WEB-INF […]

Einen Kommentar hinzufügen...

Sie müssen registriert und angemeldet sein um einen Kommentar zu schreiben.