TopLogic - the automated application engine
  • Releases
  • Dokumentation
  • Github
  • Discord
  1. Home
  2. Releases
  3. TL_8.0.0-alpha8
  4. #29460

8.0.0-alpha8
TopLogic Release

2026-09-01

enhancement

major
#29404
Deklaratives <switch>-View-Element für eingabeabhängige Detailsichten
#29425
Nicht-binäre String-Spalten datenbankunabhängig case-insensitive (Collation)
#29430
Complete the headless agent / script interface of the React view layer
#29447
Nacharbeiten zu #29088: Modellbasierte Zugriffsrechte
#29448
React: Verbesserungen der Tabellen im React-Layout
#29449
QueryExecutor sichert das Ergebnis einer Skriptausführung ab
minor
#29434
PrettyPrint für Workflow-Exporte
#29445
Kommandogruppen in den Rollenprofilen stabil sortieren
#29458
Self-Service: Einmal gesendete Mail kann nicht noch einmal gesendet werden
#29464
Default-Spalten in der Instanzensicht im Modell-Editor
#29480
Login vor geplantem Wartungsmodus
#29484
Beschreibungen (Tooltips) für die Modellelemente der tl-engine

defect

critical
#29465
React form: <size-constraint> without an upper bound rejects every non-empty text
#29468
A form's editing buffer is not a TLFormObjectBase, so identity-based constraints reject every edit
major
#29446
React: Zustand einer Seite über einen Reload hinweg erhalten
#29460
IllegalStateException "Control has no id!" beim Öffnen eines Dialogs bei offenem Popup-Dialog
#29492
Spalte LAYOUT_KEY zu kurz in TEMPLATE_LAYOUTS und LAYOUT_CONFIGURATIONS
#29493
Unnötige Datenbankabfragen bei Löschung von Objekten für historische Referenzen.
minor
#29423
Umgang mit Nutzernamen
#29427
ReactSplitPanelControl propagiert attach/detach nicht an seine Panes
#29456
Option "Zeilenobjekte verwenden" liefert GridTreeTableNodes anstatt Fachobjekte in Tabellenkonfiguration
#29457
Einheitliche Namen für Applikations-Logos
#29461
CommandDispatcher soll Gültigkeit des Ziel-Modells des Kommandos prüfen

update

critical
#29454
Update dependency org.jsoup:jsoup to v1.23.1 [SECURITY]
#29470
Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.3 [SECURITY]
defect (Nutzer-sichtbar)

major

#29460

IllegalStateException "Control has no id!" beim Öffnen eines Dialogs bei offenem Popup-Dialog

Symptom

Beim Ausführen eines Gotos (bzw. allgemein beim Öffnen eines Dialogs) tritt sporadisch folgender Fehler auf:

com.top_logic.util.error.TopLogicException: Internal error
	at com.top_logic.mig.html.layout.CommandDispatcher.internalDispatchCommand(CommandDispatcher.java:218)
	...
Caused by: java.lang.IllegalStateException: Control has no id!. Method is called before initial write occured, fetchID() was called, or after detach.
	at com.top_logic.layout.basic.AbstractControlBase.getID(AbstractControlBase.java:380)
	at com.top_logic.layout.structure.BrowserWindowControl.unregisterAndClosePopupDialog(BrowserWindowControl.java:420)
	at com.top_logic.layout.structure.PopupDialogControl.closePopupDialog(PopupDialogControl.java:345)
	at com.top_logic.layout.structure.PopupDialogControl.handleDialogClosed(PopupDialogControl.java:388)
	at com.top_logic.layout.structure.DialogModel$1.internalDispatch(DialogModel.java:41)
	...
	at com.top_logic.layout.structure.DefaultPopupDialogModel.setClosed(DefaultPopupDialogModel.java:175)
	at com.top_logic.layout.structure.BrowserWindowControl.unregisterAndCloseAllPopupDialogs(BrowserWindowControl.java:477)
	at com.top_logic.layout.structure.BrowserWindowControl.openDialog(BrowserWindowControl.java:307)
	at com.top_logic.mig.html.layout.DialogSupport.registerOpenedDialog(DialogSupport.java:84)
	at com.top_logic.tool.boundsec.OpenModalDialogCommandHandler.internalOpenDialog(OpenModalDialogCommandHandler.java:335)
	...
	at com.top_logic.tool.boundsec.commandhandlers.GotoHandler.handleCommand(GotoHandler.java:136)

Ursache

In BrowserWindowControl.popupDialogs bleibt ein bereits detachtes PopupDialogControl (ohne ID) als "offenes Popup" stehen.

unregisterAndClosePopupDialog() setzt voraus: "steht in popupDialogs" und "steht nicht in popupDialogsToOpen" impliziert "wurde geschrieben und ist attached" -- und ruft deshalb aDialog.getID() auf. Diese Invariante kann brechen:

  1. Popups hängen im LocalScope des BrowserWindowControl: updatePopupDialogs() wird aus internalRevalidate() innerhalb von context.validateScoped(this, ...) aufgerufen, und UpdateWriter.add() schreibt die FragmentInsertion sofort. Das Popup attacht sich damit an das BrowserWindowControl. Anders als Dialoge bekommen Popups keinen eigenen getLayerScope().
  2. Bei einem kompletten Repaint des Fensters (requestRepaint()) laufen internalRequestRepaint() -> BrowserWindowControl.detachInvalidated() -> dropIncrementalUpdates() (leert popupDialogsToOpen/`popupDialogsToClose`) und anschließend clearLocalScope() -> alle Popups werden detacht (id == null).
  3. Der Repaint rendert über den BrowserWindowRenderer neu -- und der schreibt den Popup-Anker pdlgPopupDialogs leer. Dialoge werden aus dialogs neu gerendert (writeDialogs()), Popups werden nirgends neu geschrieben.
  4. popupDialogs wird dabei nie geleert. Das Popup ist am Client weg, detacht, ohne ID -- steht aber weiter in der Liste, und sein Listener am PopupDialogModel ist noch registriert (deregisterAsListener() passiert nur in closePopupDialog()).

Beim nächsten Dialog-Öffnen läuft unregisterAndCloseAllPopupDialogs() über diesen Zombie-Eintrag, setClosed() feuert, handleDialogClosed() -> closePopupDialog() -> unregisterAndClosePopupDialog() -> getID() -> Exception.

Denselben Effekt hat der zweite Pfad, der im Code sogar explizit kommentiert ist (attachRevalidated(): "Drop updates for dialogs and popups that are potentially added in detached state"): Ein Popup, das im detachten Zustand geöffnet wird, landet in popupDialogs und in popupDialogsToOpen; beim nächsten Attach wird nur popupDialogsToOpen geleert -- der Eintrag in popupDialogs bleibt als nie attachter Zombie zurück.

Der Goto ist nur der Auslöser, der über die verrottete Liste stolpert. Der Schaden entsteht typischerweise in einem früheren Request (Layout-Neuaufbau, Theme-/Sprachwechsel, Invalidierung des Fenster-Controls) während ein Popup-Menü offen war.

unregisterSinglePopupDialog(String) ist von derselben Invariante betroffen: Dort wird popup.getID() über alle Einträge von popupDialogs aufgerufen.

Beispiel zur Reproduktion

  • Burger-Menu öffnen.
  • F5 drücken
  • Goto ausführen.

Lösung

  • Ursache: In attachRevalidated() und detachInvalidated() werden die offenen Popups zusätzlich zu den Inkrement-Listen verworfen (unregisterAllPopupDialogs() -- entfernt aus popupDialogs, schließt und detacht, ohne Client-Update). Das entspricht der Realität: Popups überleben ein Neuzeichnen des Fensters nicht.
  • Defense in depth: unregisterAndClosePopupDialog() und unregisterSinglePopupDialog() rufen getID() nur noch für attachte Popups auf.

Test

  • test.com.top_logic.layout.structure.TestBrowserWindowControl
  • Get Started
  • Github
  • Discord
  • Das Unternehmen hinter TopLogic
  • Softwareentwicklung heute
  • Kontakt

© Copyright – Business Operation Systems GmbH

  • top-logic.com
  • Nutzungsbedingungen
  • Impressum
  • Rechtlicher Hinweis
  • Datenschutz
  • EN
  • Login