FacesTales

Enterprise Java ohne EJB

Lars Röwekamp und Matthias Weßendorf

Im Jahr 2002 veröffentlichte Rod Johnson ein Buch mit dem Namen „J2EE Design and Development“. Dies war gleichzeitig die Geburtstunde des Frameworks, dessen Codebasis aus dem Beispielmaterial des Buchs entstand und auf Sourceforge veröffentlicht wurde. Bedingt durch die (damals) komplexen Anforderungen an J2EE-Projekte – ohne XDoclet ging nicht viel – eroberte Spring die Java-Community im Schlaf. Seitdem ist viel passiert, was Einfluss auf die Entwicklung von JSF und anderen Java-Enterprise-Anwendungen genommen hat. Auch innerhalb von Java EE.

Lightweight – das neue Unwort

Die „Leichtgewichtigkeit“ von Spring wurde zum Vorbild vieler (Software-)Autoren. Die Folge: Fast jede neue Technologie wurde als lightweight angepriesen. EJBs dagegen waren Anfang der 2000er Jahre absolut out. Nicht ohne Grund. Der Erfolg von Spring beeinflusste allerdings auch die klassische Java-Enterprise-Community inklusive der JCP Expert Groups. Der populäre defacto Standard-Stack „Hibernate und Spring“ floss nach und nach in die Java Enterprise Edition ein. Für das Java-EE-5-Release wurde die EJB-Spezifikation stark vereinfacht und JPA übernahm die Persistierung der Entitäten. Mit Java EE 6 wurde JPA als eigenständige Spezifikation vollständig aus dem EJB-Umfeld herausgelöst. Zusätzlich wurde JPA, wie von Hibernate bekannt, ein Criteria API spendiert. So weit, so gut. Doch der Zugriff auf JPA erfolgt in klassischen JEE-Anwendungen weiterhin mit – allerdings vereinfachten – EJBs. Das muss nicht sein.

JSR 299

Mit dem Release von Java EE 6 gibt es eine weitere Spezifikation (CDI), die Java-Enterprise-Entwickler erlernen müssen. Ursprünglich wurde dieser JSR, der zum Ziel hatte, das JBoss Seam Framework zu standardisieren, mit dem Namen WebBeans eingeführt. Rund um den JSR 299 gab es viel Kritik an dem neuen Komponenten- und Programmiermodel. Als Konsequenz wurden Teile des „Dependency Injection“ API in den JSR 330 (Dependency Injection for Java) ausgelagert. Die CDI-Spezifikation basiert nun auf diesem Standard-API, das ebenfalls von Spring und Google Guice unterstützt wird. Die Injection basiert auf (eigenen) Annotationen (Listing 1).

// injection-point innerhalb einer Klasse
@Inject @JavaMagazinPrintService PrintService ps;
...
// Der @JavaMagazinPrintService "Qualifier"
@javax.inject.Qualifier
@Target( { TYPE, METHOD, FIELD })
@Retention(RUNTIME)
public @interface JavaMagazinPrintService
{
}

Listing 1: Der JSR 330

An dieser Stelle sei vermerkt, dass der JSR-330 nicht standalone nutzbar ist. Sein „Injection“ API kann derzeit nur mit Spring 3, Guice2 oder eben CDI genutzt werden. Der CDI-Standard ist allerdings nicht nur die Java-EE-Brücke für den JSR 330. CDI bietet ein einheitliches scoping von Java Beans oder ermöglicht die Programmierung von Factory-Klassen/-Methoden, so genannten Producern:

@Produces @Named("myDate")
public java.util.Date getTheDateToMe()
{
  return new java.util.Date();
}

// Erzeugen eines Datum:
@Inject Date dasDatum;

Neben den „Standard“-Java-Klassen können so auch einfach Third-Party-Bibliotheken eingebunden werden. Die @Named-Annotation im Beispiel bewirkt übrigens lediglich, dass das Date-Objekt auch für die Expression Language sichtbar ist und dort unter dem Namen myDate angesprochen werden kann. Bereits die oben gezeigten Features stellen einen deutlichen Mehrwert dar. Ein besonders interessanter Punkt von CDI ist jedoch die mögliche Integration mit dem Interceptor API aus Java EE 5, die durch die neu eingeführte @InterceptorBinding-Annotation erreicht werden kann.

AOP via Java EE – Es geht auch ohne EJBs!

Mit Interceptoren kann bestimmter Code ausgeführt werden, der nicht direkt Teil des eigentlichen Programms ist. Ein klassisches Beispiel wäre das Logging: Vor und nach dem Ausführen von kritischen Transaktionen sollen Informationen in ein Logfile geschrieben werden. Damit die Geschäftslogik nicht aufgebläht wird, schreibt man einen Interceptor für diesen Code.

Im Folgenden schauen wir uns an, wie wir das Transaction API von JPA mithilfe eines Interceptors „verstecken“. Dies hätte den Vorteil, dass JPA inkl. Transaktionen auch ohne EJB SessionBeans problemlos innerhalb eines Java EE Containers aufgerufen werden kann. Ähnlich wie bei Spring benötigen wir dafür nur eine eigene @Transactional-Annotation (Listing 2).

package de.javamagazin;

import java.lang.annotation.*;
import javax.enterprise.inject.Default;
import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})

public @interface Transactional
{
  @Nonbinding Class> qualifier() default Default.class;
}

Listing 2: @Transactional InterceptorBinding

Diese Annotation wird später auf Klasse- oder Methodenebene innerhalb einer DAO oder Serviceklasse genutzt. Nun fehlt noch der eigentliche Interceptor, der später die EntityTransaction startet und auch wieder beendet (Listing 3).

@de.javamagazin.Transactional
@javax.interceptor.Interceptor
public class TransactionalInterceptor implements Serializable
{
  private @Inject EntityManager entityManager;

  @AroundInvoke
  public Object invoke(InvocationContext context) throws Exception
  {
    EntityTransaction transaction = entityManager.getTransaction();
    try
    {
      if (!transaction.isActive())
      {
        transaction.begin();
      }
     // Aufruf des eigentlichen CODE
     return context.proceed();
   }
    catch (Exception e)
    {
      if (transaction != null)
      {
        transaction.rollback();
      }
      throw e;
    }
    finally
    {
      if (transaction != null && transaction.isActive())
      {
        transaction.commit();
      }
    }
  }
}

Listing 3: AOP: Der Interceptor für die EntityTransactions

Die Interceptormethode (@AroundInvoke) startet die Transaktion und ruft die Methode auf, die mit der @Transaction annotiert wurde (siehe weiter unten). Anschließend wird die Transaktion im finally-Block ordnungsgemäß beendet. Tritt ein Fehler auf, so wird die Transaktion zurückgerollt.

Damit der Interceptor von der genutzten CDI-Implementierung gefunden wird, muss er innerhalb der beans.xml-Datei (<interceptors>) konfiguriert werden. Das Anwenden der neuen Annotation, inklusive Interceptor, ist kinderleicht:

...
  @de.javamagazin.Transactional
  public User saveUser(User user)
  {
    return dao.persist(user);
  }
...

Ähnlich wie bei der Verwendung von Spring wird die Annotation an den Stellen genutzt, wo ein Service auf eine DAO-Klasse zugreift. Der Service kann, wie bisher, von einer JSF-Bean aufgerufen werden. Am Stack ändert sich fast nichts, außer dass man nun auf EJBs – als Lieferant für eine Transaktionsklammer – verzichten kann.

Fazit und Ausblick

In der Vergangenheit wurden EJBs oft kritisiert. Mit Spring ist parallel ein starker Konkurrent erwachsen, der zeitweise klar die Federführung in der Java-Enterprise-Welt übernommen hatte. Nach und nach wurde allerdings – auch oder vor allem dank der Ideen der Open-Source-Java-Enterprise-Communities – die unnötige Komplexität aufgebohrt. Mit Java EE 6 und der darin enthaltenden EJB-3.1-Spezifikation ist mittlerweile ein Standard entstanden, der wieder wettbewerbsfähig ist. Wie oben skizziert, lässt sich bei Bedarf sogar vollständig auf EJBs verzichten und trotzdem, mit minimalem Aufwand, eine automatische Transaktionssteuerung realisieren. EJBs bekommen somit dank CDI „Konkurrenz“ aus dem eigenen Lager. Innerhalb seiner Entstehungsphase wurde der JSR 299 häufig kritisiert. Nach und nach wird jedoch klar, dass mit CDI ein äußerst komfortables und mächtiges Werkzeug innerhalb der Java-EE-Welt zur Verfügung steht. Etablierte Projekte und Frameworks, z. B. das Naked Objects Framework, das mittlerweile an die Apache Software Foundation übergeben wurde, wollen zukünftig auf CDI als Programmiermodel umstellen. Apache MyFaces CODI bietet übrigens bereits Transactional Support an und auch für Java EE 7 ist eine Überarbeitung des Transaktionsverhaltens von EJBs erwartet.

Lars Röwekamp ist Geschäftsführer der open knowledge GmbH und berät seit mehr als zehn Jahren Kunden in internationalen Projekten rund um das Thema Enterprise Computing (Twitter: @mobileLarson).

Matthias Weßendorf arbeitet für die Firma Kaazing. Dort beschäftigt er sich mit WebSocket, HTML5 und weiteren Themen rund um das „Next Generation Web“. Matthias bloggt regelmäßig auf http://matthiaswessendorf.wordpress.com (Twitter: @mwessendorf).

Geschrieben von
Lars Röwekamp und Matthias Weßendorf
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.