major
#29581
TopLogicServlet: Vertrag dokumentieren - eine Anfrage hat nur im Fenster einen Benutzer
Found on 2026-09-13 against tl-core 8.0.0-alpha8, while closing issue #169 of the BOS/Consulting application.
Observed
com.top_logic.util.TopLogicServlet is the framework's "a valid session is required" base. For a request that carries one it calls
TLContextManager.inInteraction(sessionContext, getServletContext(), request, response, …)
which installs the session context only. The sub-session stays null for a request that addresses no window. TLContext#getPerson() reads the sub-session, so such a request
- acts as nobody: TLContext.isAnonymous() answers true for a logged-in user, and since 8.0.0-alpha7 model reads answer the attribute's empty value because BoundChecker.isAllowedBypass returns false without an acting person;
- cannot read the knowledge base at a session revision:
Cannot invoke "com.top_logic.base.context.TLSubSessionContext.getSessionRevision(
com.top_logic.knowledge.service.HistoryManager)" because "subSession" is null
The BOS/Consulting application ran into this with servlets of its own derived from TopLogicServlet (AuthenticatedServlet as base of DocumentPdfServlet, ClientFileServlet, ConsultingHtmlPageServlet) and worked around it by creating a sub-session for TLSessionContext#getOriginalUser() under a fixed key. Reported from BOS/Consulting (issue #130).
Analyse
Der Benutzer, für den eine Anfrage handelt, gehört nicht zur Session, sondern zur Sub-Session eines Browser-Fensters. ContentHandlersRegistry.startLogin legt die Sub-Session jedes Fensters mit dem für dieses Fenster angegebenen Benutzer an; die Mehrbenutzer-UI-Tests der klassischen Oberfläche arbeiten in einer Session mit einem anderen Benutzer je Fenster. TLSessionContext#getOriginalUser() ist daher nicht "der Benutzer der Anfrage", und eine Anfrage, die kein Fenster adressiert, hat keinen definierten Benutzer. Eine sitzungsweite Default-Sub-Session für den ursprünglichen Benutzer (PR !1815, ohne Merge geschlossen) würde eine solche Anfrage für einen geratenen Benutzer ausführen.
Im Framework brauchen die Anfragen ohne Fenster keinen Benutzer: subsession.jsp, die Fehlerseite ThrowableErrorPage.jsp und ResourceViewerServlet lesen keine Daten der Wissensbasis. Inhalte, die vom Benutzer abhängen, werden innerhalb eines Fensters ausgeliefert: im React-UI über ReactServlet /data mit einem DataProvider-Control, im klassischen UI über einen im Scope des Fensters registrierten ContentHandler. Servlets einer Anwendung, die benutzerabhängige Inhalte ohne Fenster ausliefern, sind auf diese Wege umzustellen.
Lösung
- Der Klassenkommentar von TopLogicServlet beschreibt den Vertrag: eine Anfrage mit Session läuft ohne Sub-Session; Benutzer und Lese-Revision gehören zur Sub-Session des adressierten Fensters, die ein fensterbezogenes Servlet selbst installiert; ohne Fenster ist TLContext#currentUser() null, benutzerabhängige Inhalte werden innerhalb eines Fensters ausgeliefert. Include und Forward behalten die Sub-Session der äußeren Anfrage.
- Der Kommentar von TLContext#currentUser() sagt, dass der Benutzer der des Fensters ist und ohne Sub-Session null.
- TLContext#getPerson() wirft weiterhin nicht: Hintergrund- und System-Threads laufen legitim ohne Sub-Session.