Stateful SessionBeans gehen verloren

musiKk

Top Contributor
Hallo Forum,

ich fuhrwerke zur Zeit mit SFSBs herum und irgendwie klappt das nicht so ganz.

Hintergrund: Eine ehemalige SE-Anwendung soll per Webservice ansprechbar sein. Dazu habe ich Teile in EJBs umgewandelt. Da ich nun auf eine Klasse gestoßen bin, die über mehrere Methoden hinweg einen Zustand behalten muss (das DAO; ist zwar ziemlich bescheiden, aber das zustandslos zu machen, wäre ein ziemlich großer Aufwand), reicht hier die oft verwendete Stateless SessionBean nicht aus. Grob gesehen sieht das so aus:
Java:
@Stateless
public class MyBeanImpl implements MyBean {
	@EJB
	private MyDao dao;

	public Result doStuff(Stuff stuff) {
		try {
			dao.setUpStuff(stuff);
			Result result = new Result();
			/* mehr komplexes Zeugs mit dem DAO und Aufbau des Result-Objekts */
			return result;
		} finally {
			dao.finish();
		}
	}
}
Die Methode [c]finish()[/c] ist dabei eine mit [c]@Remove[/c] annotierte Methode. Die Methode [c]doStuff()[/c] wird von einem Webservice aufgerufen - insofern ist das mit der SFSB ziemlich doof, weil ich ja keine Zustand über mehrere Requests von außen will, sondern innerhalb eines Requests. Wenn es dafür schönere Lösungen gibt, dann interessiere ich mich natürlich auch dafür. Es wäre zwecks DI schön, wenn die Bean halt eine bleiben könnte. Das sieht etwa so aus:
Java:
@WebService
public class MyWebServiceImpl implements MyWebService {
	@EJB
	private MyBean myBean;
	
	public Result doStuff(Stuff stuff) {
		return myBean.doStuff(stuff);
	}
}

Das eigentliche Problem: Diese Sache klappt etwa 50/50. Z. B. kann ich mal vier Anfragen parallel an den Service stellen und alles geht. Beim nächsten Mal schlagen die gleichen Anfragen fehl. Oder manchmal auch nur die Hälfte. Der Fehler ist dabei immer:
Code:
Caused by: javax.ejb.NoSuchEJBException
	at com.sun.ejb.containers.BaseContainer.mapBusinessInterfaceException(BaseContainer.java:1522)
	at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1430)
	at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1325)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:205)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:83)
	at $Proxy215.close(Unknown Source)
	...
Caused by: javax.ejb.NoSuchObjectLocalException: The EJB does not exist. session-key: 1907f0100001f-ffffffffaacf51e3-9
	at com.sun.ejb.containers.StatefulSessionContainer._getContext(StatefulSessionContainer.java:1220)
	at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:1709)
	at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1238)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:195)
	... 86 more

Die Fehlermeldung gibt zwar einige Treffer bei Google, aber zu einer Lösung hat mir das bisher noch nicht verholfen. Vielleicht hat ja jemand eine Idee.

Gruß
mK
 

musiKk

Top Contributor
Ach whatever... jetzt ist das DAO halt kein Bean mehr und die Injection der Datenbankverbindung muss manuell geschehen...
 

musiKk

Top Contributor
Kannst mir ja eine geben. Das ist nicht patzig gemeint, ich weiß wirklich nicht, wie ich das anders lösen kann. Der Umstand ist nicht riesig, darum sehe ich das noch nicht als schlimmen Hack an. Schön wäre aber vermutlich trotzdem anders.
 

FArt

Top Contributor
Ob deine angedachte Migrationsstrategie gut oder nicht ist, kann ich nicht beurteilen, dafür habe ich zu wenig Informationen. Aber zur Lösung des geposteten Problems schlage ich folgenden Weg vor:

Mal geht es, mal geht es nicht... Vermutung: Zeitverhalten.
Der Lebenszyklus eines SFSB ist im Container konfigurierbar (oder über den containerspezifischen Deploymentdeskriptor). Hier kann man angeben, wie lange es dauert, bis der Container auf die Idee kommt ein SFSB zu passivieren bzw. zu entfernen.

Schalte das Logging deines Containers entsprechend ein, so dass du nachvollziehen kannst, wann das SFSB welchen Status einnimmt. Danach überprüfe, warum das der Fall ist und treffe Gegenmaßnahmen. Debugging wäre auch eine Möglichkeit, aber Logging sollte bereits ausreichend sein.
 

musiKk

Top Contributor
Ob deine angedachte Migrationsstrategie gut oder nicht ist, kann ich nicht beurteilen, dafür habe ich zu wenig Informationen.

Vermutlich nicht so sehr. Ich finde es schon ungewöhnlich, ein DAO mit Zustand zu haben. Nur das Ding ist riesig, es ist schwer zu überschauen, welche Änderungen einmal welche Nebeneffekte haben könnten (Tests oder Doku gibts natürlich nicht) und da das System relativ kritisch ist, versuche ich Änderungen sehr überschaubar zu halten.

Der Lebenszyklus eines SFSB ist im Container konfigurierbar (oder über den containerspezifischen Deploymentdeskriptor). Hier kann man angeben, wie lange es dauert, bis der Container auf die Idee kommt ein SFSB zu passivieren bzw. zu entfernen.

Logging an den entsprechenden Stellen habe ich durch. Die Einstellungen zur Passivation sind bei zehn Minuten. Die Requests waren in meinen Tests (alleine aus Zeitgründen) immer darunter. Da das SFSB während seiner gesamten Lebensdauer aber ganz schön in Beschlag genommen wird, sollte der Container auch sonst eigentlich nicht auf die Idee kommen, es einfach zu passivieren. Ressourcen gibt es genug. Log-Meldungen in den entsprechenden Callbacks ([c]@PrePassivate[/c], [c]@PostActivate[/c]) bestätigten mir auch, dass keine Passivation stattfindet.

Im Nachhinein habe ich mich auch gewundert, warum die Methode mit der SFSB überhaupt mal funktioniert hat. Schließlich brauche ich bei mehreren Anfragen an den Web Service auch mehrere Instanzen der Bean. Stateful Beans sind nach meinem Verständnis aber dazu da, auch über mehrere Anfragen beibehalten zu werden. Wie man die Semantik einer Session nun genau beeinflussen kann, habe ich auch noch nicht raus. So wirklich drin bin ich in der JEE-Sache leider immer noch nicht...

edit: Typo...
 
Zuletzt bearbeitet:

FArt

Top Contributor
Passivierung kann nicht das Problem sein, das Bean würde ja bei Zugriffen wieder aktiviert werden.
Wenn ein Bean mit ID X nicht mehr existiert, gibt es irgendwo ein Logging, dass SFSB mit ID x entfernt wurde. Es gilt also herauszufinden wer warum auf die Idee kommt, das Bean zu löschen.

Möglichkeiten: explizites beenden (sollte jeder machen wenn er fertig ist), Timeout oder nicht behandelter Fehler (nicht deklarierte Exception).

SFSB werden über mehrere Anfragen (in der Regel eines Clients) benötigt. Wenn man ein SFSB anders verwendet (z.B. ein SFSB pro Service, auf den mehrere Clients zugreifen) muss man wissen, was man tut. Wer hält in deinem Fall den Handle auf das SFSB bzw. den Stub darauf? Ist das SFSB per Client? Oder für den ganzen Webservice?

So wirklich drin bin ich in der JEE-Sache leider immer noch nicht...
Das ist ein großes Problem. Das sage ich nicht, weil ich stänkern möchte, sondern weil ich schon viele "Java Implementierungen" in einer Enterpriseumgebung finden und reparieren durfte. Für mich macht sich dieses verbreitete Verhalten bezahlt ;-)
Die Firmen zahlen bitteres Lehrgeld... oft inklusive Ausfallzeiten, Imageverlust, Berater, ...
 
Zuletzt bearbeitet:

musiKk

Top Contributor
Passivierung kann nicht das Problem sein, das Bean würde ja bei Zugriffen wieder aktiviert werden.
Wenn ein Bean mit ID X nicht mehr existiert, gibt es irgendwo ein Logging, dass SFSB mit ID x entfernt wurde. Es gilt also herauszufinden wer warum auf die Idee kommt, das Bean zu löschen.

Möglichkeiten: explizites beenden (sollte jeder machen wenn er fertig ist), Timeout oder nicht behandelter Fehler (nicht deklarierte Exception).

Ja, eigentlich wird das immer explizit erledigt (im finally-Block).
Nach dem Container-Logging werde ich bei Bedarf nochmal schauen.

SFSB werden über mehrere Anfragen (in der Regel eines Clients) benötigt. Wenn man ein SFSB anders verwendet (z.B. ein SFSB pro Service, auf den mehrere Clients zugreifen) muss man wissen, was man tut. Wer hält in deinem Fall den Handle auf das SFSB bzw. den Stub darauf? Ist das SFSB per Client? Oder für den ganzen Webservice?

In meinem Fall ist es ja noch schlimmer: Mehrere Anfragen pro Client parallel und jede Anfrage benötigt solch ein DAO mit Zustand und es ist wichtig, dass sich die verschiedenen Anfragen nicht ins Gehege kommen.

Das Bean [c]MyBean[/c] (seines Zeiches [c]@Stateless[/c]) hält dabei die Referenz auf den Stub des DAO.
Der Ablauf ist im Prinzip folgender: Der Service erhält pro Anfrage eine Instanz des [c]MyBean[/c] und dieses benötigt das berüchtigte DAO - jede Anfrage und damit jede [c]MyBean[/c] sein eigenes. Darauf wird ein paar Mal darauf zugegriffen, wobei der Zustand gewahrt werden muss. Auf diese Weise wird ein komplexeres Ergebnis erzeugt, welches vom Web Service wieder an den Client zurückgeschickt wird. Sobald [c]MyBean[/c] dieses Ergebnis erzeugt hat, kann das DAO entsorgt werden, bzw. der Zustand ist nicht mehr von Belang, da bei der nächsten Anfrage wieder ein neues benötigt wird.

Das ist ein großes Problem. Das sage ich nicht, weil ich stänkern möchte, sondern weil ich schon viele "Java Implementierungen" in einer Enterpriseumgebung finden und reparieren durfte. Für mich macht sich dieses verbreitete Verhalten bezahlt ;-)
Die Firmen zahlen bitteres Lehrgeld... oft inklusive Ausfallzeiten, Imageverlust, Berater, ...

Passt schon, ich versuche auch nicht, mein bisheriges Unwissen zu überdecken, sondern möchte etwas dagegen tun. Das Ding ist halt: Wenn man frisch von der Hochschule in eine Firma kommt und dort als einziger so ein System nach JEE migrieren soll, dann ist das ohne Ansprechpartner eher suboptimal. Selbst mit kleinen Fragen kann ich also nicht zu Kollegen und damit bleibt mir für sowas z. B. nur das Forum.

Eine kleine Anekdote dabei: Das DAO war zunächst sogar zustandslos, weil ich fälschlicherweise davon ausgegangen bin, dass der Zustand gewahrt bleibt, wenn ich über die selbe Referenz auf das Objekt zugreife. Aber Pustekuchen, der Container tauscht das Objekt auch gerne aus - dadurch sind Einzelanfragen immer glatt durchgelaufen, parallele wurden aber nicht reproduzierbar vermengt. Ich wusste zwar vorher auch schon um die Proxies und im Nachhinein ists auch irgendwie logisch, aber das wäre eben auch so eine Sache, die einem jemand mit etwas Erfahrung in zwei Minuten hätte mitteilen können.

Specs lesen hilft dabei dann auch nur bedingt (es hilft schon, aber Anleitungen sinds ja auch nicht). Darum suche ich mir das ganze über Bücher und Google zusammen. Und dann fehlen mir immer noch solche Dinge wie Best Practices. (Nur so als Disclaimer: Auch wenn man das bei meiner bisherigen Leistung vermuten könnte: Das Bean heißt natürlich nicht [c]MyBean[/c]; ich bin nur unkreativ im "zensieren")

So, genug ausgeheult. ;)
 

Ähnliche Java Themen

Neue Themen


Oben