Seam ist eine Produkt von JBoss und bedeutet soviel wie Saum, Naht. Es handelt sich um ein Framework für die Entwicklung von Web-Applikationen, das auf Java Server Faces aufsetzt.
Die ersten Gehversuche mit Application- und Session-Kontexten führten recht schnell zum Erfolg. Ergebnis war eine Anwendung, die mit weniger Dateien und weniger Komplexität auskommt als eine reine JSF-Anwendung. Ein sehr leidiges und bislang ungelöstes Thema war das Öffnen mehrere Browser-Fenster oder -Tabs mit denselben Inhalten. In klassischen Web-Applikationen werden alle Datenobjekte in der Session gespeichert, falls nicht explizit eine andere Lösung implementiert wird. Damit ist z.B. das Bearbeiten mehrerer Hoteleinträge aus einer Liste nicht möglich, da sich die offenen Browser-Fenster gegenseitig beeinflussen würden. Seam bietet dafür eine Lösung und diese heißt Conversations. Diese Konversationen stellen Kontexte dar, die verschachtelt auftreten dürfen und den Zustand der Web-Applikation zu einem bestimmten Zeitpunkt festhalten. Damit können beliebig viele Browser-Fenster gleichzeitig geöffnet sein und der Klick in jedes Fenster führt zu dem gewünschten Ergebnis. Ein ebenso altes Thema, das damit erschlagen wird, sind Back- und Forward-Operationen mit Hilfe der Browser-Navigation. Viele Applikationen versuchen durch JavaScript-Funktionen diese Navigation zu verhindern bzw. zu umgehen. Die erste umfassende Lösung wird jedoch durch Seam präsentiert. Die Implementierung ist sehr leichtgewichtig, sodass eine Reihe von Konversationen nebeneinander gehalten werden können. Die Konversationen haben einen kürzeren Timeout als die Benutzer-Session, sodass die Ansammlung größerer Datenmengen im Hauptspeicher unterbleibt.
Nach dem schnellen Erfolg mit den einschichtigen Konversationen wagte ich mich an die verschachtelten Konversationen (nested Conversations) heran. Seam-Intern werden Konversationen durch die Klasse ConversationEntries gehandhabt als eine Map. Diese Map enthält als Schlüssel die Identitäten der Konversationen und als Werte die Konversationen selbst. Die Identitäten der Konversationen werden normalerweise durch einen globalen Generator erzeugt, der jeder neuen Konversation einfach eine neue Ganzzahl zuweist. Bei Bedarf können die Identitäten auch durch eine Methode gesetzt werden. Darüber hinaus enthält jede Konversation einen Verweis auf die Identität einer Parent-Konversation, sodass die Gesamtstruktur einem Baum entspricht. Die Wurzel ist durch eine Konversation ohne Parent-Identität festgelegt. Als eine Folge davon können mehrere voneinander unabhängige Wurzelknoten und damit unabhängige Bäume existieren. Wichtig ist, dass es zu jeder Zeit einen aktuellen Knoten der Konversation gibt, diesen erhält man durch Conversation.instance(). Durch wiederholten Zugriff auf die übergeordneten Knoten kann die Abfolge bis zur Wurzelkonversation rekonstruiert werden. Diese Liste wird auch als Konversation-Stack bezeichnet. Der Sinn dieses relativ aufwändigen Konstrukts besteht im Management so genannter Concurrent Conversations. Wurde z.B. ein Hotel selektiert, können daraus wiederum mehrere Zimmer selektiert und auf einer anderen Web-Seite konfiguriert werden.
Da noch sehr wenig Dokumentation zur Verwendung dafür verfügbar ist, lief ich anfangs zunächst in eine Sackgasse. Fälschlicherweise übersah ich den Zusammenhang zwischen Konversationen und den Views (d.h. den Web-Seiten). Ich versuchte den Konversationen Namen zu geben und baute Methoden, die auf dem Konversation-Stack anhand der Namen herumsprangen.