Warum Stateful EJB, statt normaler Klasse?

Hallo zusammen,

ich habe Schwierigkeit, den Sinn einer Stateful Ejb zu verinnerlichen. Ich habe verstanden, dass der Zustand der Bean gemerkt wird, aber bei einem Java-Objekt wird der Zustand doch auch gemerkt. Warum möchte man Stateful statt normalem Java-Objekt verwenden? Beispiel:

service = (EmployeeServiceRemote) new InitialContext().lookup("EmployeeBean/remote");

service.addItem("item", 10);
service.removeItem("item", 1);
service.checkout(10
...

Ich habe im Internet den obigen Code gefunden. EmployeeBean wird als Stateful-Bean definiert:

public class EmployeeBean implements EmployeeServiceLocal, EmployeeServiceRemote {


  public EmployeeBean() {
  }

  private HashMap<String,Integer> items = new HashMap<String,Integer>();

  public void addItem(String item, int quantity) {
      Integer orderQuantity = items.get(item);
      if (orderQuantity == null) {
          orderQuantity = 0;
      }
      orderQuantity += quantity;
      items.put(item, orderQuantity);
  }

  public void removeItem(String item, int quantity) {
      Integer orderQuantity = items.get(item);
      if (orderQuantity == null) {
          return;
      }
      orderQuantity -= quantity;
      if (orderQuantity > 0) {
          items.put(item, orderQuantity);         
      } else {
          items.remove(item);
      }
  }
  
  public Map<String, Integer> getItems() {
      return items;
  }

  @Remove
  public void checkout(int paymentId) {
    System.out.println("checkout");
  }

  @Remove
  public void cancel() {
  }


}

Ganz konkrett:
wenn wir hier auf @Stateful verzichten würden und in dem Client-Code einfach new EmployeeBean(); verwenden, geht das doch auch oder?

Wo ist hier mein Denkfehler?

Vielen lieben Dank aus Rheinland,

Eure Ratna:)

Weil eine neue EmployeBean keine „vorherigen“ Zustand kennt.
items waere immer leer bei einer neuen Bean.

Zudem handelt es sich um EJB’s. Diese unterliegen ihrem eigenen Lifecycle.

Das “checkout” wird auf dem Server ausgegeben und nicht auf dem Client! Das ist ein wesentlicher Unterschied.

Hallo maki,

vielen Dank erstmal für die rasche Antwort. Eine neue EmployeBean würde keine vorherigen Zustände kennen. Ich weiss es nicht, ob ich hier etwas Grundsätzliches verpasst habe oder nicht verstanden habe. Aber es scheinen mir folgende 2 Codes gleich zu sein. Könntest Du vielleicht dazu etwas kommentieren?

service = (EmployeeServiceRemote) new InitialContext().lookup("EmployeeBean/remote");

service.addItem("item", 10);
service.removeItem("item", 1);
service.checkout(10
...
EmployeeBean service = new EmployeeBean();

service.addItem("item", 10);
service.removeItem("item", 1);
service.checkout(10
...

Vielen Dank

Viele Grüße aus Rheinland,

Eure Ratna

  • Mit einer eigenen Klasse könntest du während deiner Sitzung 10000x den Konstruktor aufrufen und 10000 Objekte erzeugen.
  • Du kannst die Beans Injecten, da sie vom Container verwaltet werden, d.h. du brauchst keine Objekte hin und her zu schieben sondern einfach per Annotation / InitialContext “injecten”

Hallo pl4gu33,

vielen Dank erstmal für den Beitrag. Ich verstehe was Du meinst, allerdings zumindest im folgenden Code es lediglich einmal der Konstruktor aufgerufen und es existiert lediglich ein Objekt, oder?

EmployeeBean service = new EmployeeBean();

service.addItem("item", 10);
service.removeItem("item", 1);
service.checkout(10
...

Ich bin seit Stunden auf der Suche nach einem Beispiel, wo ich den Vorteil von einer Stateful-EJB im Vergleich zu einem “new” erkennen kann. Oder ganz konkrett, wo ich sehen kann, dass beim normalen “new” mehrfach Objekte erstellen werden müssen, wohingegen mit einer Stateful-EJB lediglich eine Instanz zum Einsatz kommt. Hättest Du vielleicht so ein Beispiel-Code?

Zum Festhalten: beim obigen Code wäre die “new”-Variante genauso effektiv wie bei der Stateful-EJB-Variante. Liege ich richtig?

Viele Grüße aus Rheinland,

Eure Ratna

Ich frage mal andersrum… warum willst du sozusagen alles selber “verwalten”, wenn der Container sozusagen alles für dich übernimmt und es auch richtig macht.
Er managed den Lifecycle, weiß wann er die Bean zerstören muss, wann er sie laden muss, etc. etc.

Das kann dir keiner beantworten, da bei EJBs z.B. Proxy-Objekte generiert werden auf die dann zugegriffen wird. Wenn du anfängst das selber übernehmen zu wollen, können da z.B. Memory Leaks entstehen, da gemischt wird. Das mag erst auftreten, wenn 10 Zugriffe die Sekunde aufkommen aber vom Prinzip sollte man, keine Konstruktoren aufrufen. (Alles unter Vorbehalt, selbst noch im Lernprozess)

Hallo pl4gu33 und timbeau,

nein, bitte nicht falsch verstehen. Letzendlich würde ich nur gerne verstehen, was ein Stateful-EJB ist. Im Internet gibt es zahlreiche Beispiele, allerdings konnte ich noch keins finden, wo ich den AHA-Effekt erkennen konnte, dass durch die lediglich eine Instanz viel Resources gespart werden.

Ich glaube, es gibt eine Sache, die ich noch nicht verstehe:
Es wird oft gesagt, dass der Zustand der Kommunikation zwischen dem Client und der Bean gespeichert wird. Ich weiss ehrlich gesagt nicht, wie genau damit gemeint ist. Ich nehme an, damit sind insbesondere der “Inhalt” der Members der Instanz gemeint. Aber schaut Euch bitte diesen Code an:

EmployeeBean service = new EmployeeBean();

service.addItem("item", 10);
service.removeItem("item", 1);
service.checkout(10
...

Beim obigen Code hat man doch auch immer ge"up-datete" Werte nach jeder Zeile, oder? Es sind gewöhnliche Methodenaufrufe.

Oder ist das so, dass bei einer Server-Client-Anwendung jede Methodenaufruf immer mit einem neuen Objekt verbunden ist?

Vielen Dank für Eure Hilfestellungen.

Viele Grüße aus Rheinland,

Eure Ratna

Du wirst den Unterschied erstmal nicht direkt erkennen können. Bei manchen Dingen versuche ich mich an die “Best Practise” zu halten. Sprich, wenn Oracle mir sagt, Konstruktoren sollten vermieden werden, fange ich persönlich nicht an, die Java EE Spezifikation auseinanderzunehmen.
Man hat auch bei einer Bean nicht jedes Mal ein neues Objekt nur weil ein Methodenaufruf auftritt.

Hallo timbeau,

okay, das nehme ich erstmal so hin. Ein schönes zugleich für mich unverständliches Beispiel habe ich soeben gefunden:


import javax.ejb.Stateful;

@Stateful
public class Counter {

    private int count = 0;

    public int count() {
        return count;
    }

    public int increment() {
        return ++count;
    }

    public int reset() {
        return (count = 0);
    }
}

import junit.framework.TestCase;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;

public class CounterTest extends TestCase {

    //START SNIPPET: local
    public void test() throws Exception {

        final Context context = EJBContainer.createEJBContainer().getContext();

        Counter counterA = (Counter) context.lookup("java:global/simple-stateful/Counter");

        assertEquals(0, counterA.count());
        assertEquals(0, counterA.reset());
        assertEquals(1, counterA.increment());
        assertEquals(2, counterA.increment());
        assertEquals(0, counterA.reset());

        counterA.increment();
        counterA.increment();
        counterA.increment();
        counterA.increment();

        assertEquals(4, counterA.count());

        // Get a new counter
        Counter counterB = (Counter) context.lookup("java:global/simple-stateful/Counter");

        // The new bean instance starts out at 0
        assertEquals(0, counterB.count());
    }
    //END SNIPPET: local
}

Verwirrendes für mich:

  1. Warum
        assertEquals(0, counterB.count());

Muss hier nicht mit einer 4 angefangen werden? Oder ist CounterB ein neuer Client? Deswegen kriegt er seine eigene Bean?

Viele Grüße aus Rheinland,

Eure Ratna

counterB ist ein neuer Client. Im übrigen lernst du vermutlich mit einer etwas veralteten Literatur. Lookups kannst du mit EJB 3.1 bzw. Java EE 6 mittels

@Inject
Counter counterA
...

counterA.increment();

realisieren.

schon gelöst

@ratnalein Was ist denn eine Stateless-Session-Bean in deinen eigenen Worten?

Möchtest du deine EJB’s über das Remote oder Local Interface ansprechen?
Soll der Client auf deine EJB’s zugreifen?

Hallo zusammen,

vielen lieben Dank erstmal für Eure Hilfestellungen.

Bis zu diesem Punkt verstehe ich es so:
Stateless → Nach JEDEM Methodenaufruf wird die entsprechende Bean weggeschmissen. Wenn nun ein weiterer Methodenaufruf bzgl. der gleichen Bean stattfindet, wird zufällig aus dem Pool die Bean rausgeholt, sie kann also die Bean von vorher sein, muss aber nicht.

Stateful → Nach JEDEM Methodenaufruf wird beim weiteren Methodenaufruf aus der gleichen EJB immer die gleiche Bean bzw. die gleiche Instanz genommen, bis die Kommunikation zwischen dem Server und dem Client beendet ist.

Die EJBs sollen übers Remote-Interface angesprochen werden. Ob die EJBs nun direkt von einem Client oder von anderen EJBs zugegriffen werden, das möchte ich erstmal als zweitrangig betrachten.

Ich bin ehrlich gesagt jetzt ein bisschen verzweifelt, da ich nun die Befürchtung habe, dass ich Äpfel mit Birnen verglichen habe. Auf der einen Seite finde ich meine anfängliche Frage vollkommen legitim, vor allem wenn man vorher lediglich mit Java SE zu tun hatte. Nun lernt man Stateful-EJB kennen, man schaut sich Beispielcodes an, man macht beim Tutorials mit, da sah man/ich: mhhh, die Klasse sieht aber irgendwie nicht viel anders als normale Klasse aus. Alle Methoden sind aufgeführt, Members sind erlaubt, usw. Was ist so besonders an dieser Stateful-EJB? Gegoogelt und gegoogelt → „conversation state“, zustandsbehaftet, usw. Konkrett → beispielsweise Anzahl der bestellten Artikel. Nachgedacht und nachgedacht, mhh… bei einer normalen Klasse bzw. Objekt wird nach einem Methodenaufruf doch immer auch die Anzahl der bestellten Artikel richtig. Was ist also wirklich der Unterschied zwischen einer Stateful-EJB und einer normalen Klasse? Genau in diesem Moment habe ich wahrscheinlich den Fehler gemacht, ich vergleiche hier ja SE und EE. Vielleicht darf ich das nicht tun? Bei SE ist „alles“ in einer Umgebung. Bei EE gibt es den Server und den Client. Es sind vielleicht 2 völlig unterschiedliche Konzepte. Die erfolglose Suche im Google nach „difference between Stateful EJB and normal java class“ bestätigt dies.

Nichts desto trotz habe ich etwas gefunden, was als gemeinsamer Nenner zwischen Stateless-EJB und normaler Klasse angesehen werden könnte:

It’s best to visualize a Stateful Session Bean (SfSB) as very close to an instance of a normal Java class. You look up (or inject) an instance of a SfSB, and the container will create one for you and return the instance. You then work with that instance like you would any other Java instance.

Also, so ganz verkehrt war mein Gedanke anscheinend doch nicht.

Wenn ich jetzt ehrlich bin, weiss ich jetzt nicht was ich eigentlich nicht verstehe. Ich kann das so hinnehmen(Arbeitsweise von Stateful-EJB ist wie die einer normalen Klasse unter SE). Zufrieden bin ich allerdings noch nicht. Ich habe das Gefühl, etwas wichtiges habe ich noch verpasst.

Vielen Dank für Eure Hilfestellungen.

Viele Grüße aus Rheinland,

Eure Ratna

Statelessbean wird bei jeder Injection “leer” übergeben. Diese hat keinen State. Wenn Du sie per @Inject einfügen lässt, bekommst Du ein sauberes Objekt.

Stateful-Bean ist für die gesamte Session des Client befüllt. Auch wenn Du sie in einer anderen Bean per @Inject einfügst, bleibt der State erhalten.

Eine Klasse kann die Aufgabe einer Stateful-Bean auch übernehmen. Dafür müsstest Du eine Sessionscoped-Bean (im Client) verwenden und dort das Objekt erzeugen und halten. Wenn Du das Objekt dann aber an anderer Stelle noch einmal erzeugst, ist der Inhalt leer.

Ist das einfach genug?

Jain, ich habe das wie folgend verstanden (wenn es falsch ist, korrigiert mich): Es KANN passieren, dass du eine bereits „benutzte“ Stateless EJB aus dem Pool erhältst. Da eine Stateless EJB nämlich (per Definition) keinen Zustand hat, kann es passieren, dass die selbe Instanz der Stateless Bean für mehrere Clients benutzt wird. Bei vielen ApplicationContainern sorgt z.B. ein ejb.remove() dafür, dass die Instanz der Bean von der Session gelöst wird und sie in den Pool zurückwandert.

Hier hatte ich auch schon mal ein ziemlich heftiges Problem bei einer „wiederverwendeten“ Stateless EJB, die mir auf einmal falsche Werte (bzw. Werte von anderen Usern) zurückgegeben hat. Instanzvariablen in einer Stateless EJB sollten nur im @PostConstruct Aufruf initialisiert und danach nicht wieder angefasst werden.

Gruß,
Tim

Grundsätzlich hat Java EE natürlich viel mit Java SE gemein. Was Beans so besonders macht findet halt nicht in der Logik einer Klasse statt und ist daher, wie auch schon gesagt, im Gesamt-SYstem zu suchen. Direkt in der Klasse sind natürlich Besonderheiten wie PostConstruct und weitere Annotation zu beachten.

Du hast natürlich recht. Deshalb auch meine Anführungszeichen. :slight_smile: Eine Statelessbean sollte natürlich keinen State beinhalten (oder dieses nur einmal initialisieren.

*** Edit ***

Interessant ist das Thema aber schon. Denn ein „Client“ der SFSB ist ja nicht gleichzusetzen mit einem „UI-Client“. Ich weiß jetzt ehrlich gesagt, was in der Spec steht, aber das oben zitierte ist so nicht unbedingt korrekt.

Da kommt natürlich die Frage auf, warum eine SFSB verwenden, wenn es auch eine normale Klasse kann. Eine normale Klasse ist sicher immer sinnvoll, wenn es denn geht. Wenn in der Klasse aber eine EnitityManager oder irgendein Context erzeugt werden muss, die nur im Backend verfügbar sind, wird es schwer. Zusätzlich bekommt man alle schönen EJB-Eigenheiten wie Transaktionssicherheit, Treadsafe, … geschenkt.

Meine Regel wäre: Immer eine einfache Klasse bevorzugen, wenn es geht. Und bisher komme ich immer ohne SFSB aus.

In verteilten Systemen wird es schwierig mit zustamndsbehafteten POJOs wenn die CLients und die POJOs selber ploetzlich „ganz woanders“ sind im Sinne von anderer Rechner, Cluster, etc. pp…