enhancement
major
minor
defect
critical
major
minor
minor
#30191
Die Modulabschaltung führt zu keiner Abschaltung des Moduls, wenn die Klasse seines InContext-Jobs nicht geladen werden kann (die Datenbank bleibt in der JVM des Containers geöffnet).
Problem
Wenn eine Anwendung in Tomcat gestoppt wird, nachdem ihre entpackten JAR-Dateien gelöscht wurden (zu finden im Debian-Paket von E-Home, haumacher/ehome#81: prerm den Kontextdeskriptor entfernt und dpkg/postrm die WAR-Datei sowie das entpackte Verzeichnis gelöscht hat, bevor Tomcat die Anwendung deinstalliert hatte), bricht der Modul-Shutdown ab, bevor auch nur ein einziges Modul gestoppt wurde:
SEVERE [Catalina-utility-1] StandardContext.listenerStop Ausnahme beim Senden des Ereignisses „Context destroyed“ an die Listener-Instanz der Klasse [com.top_logic.element.ElementStartStop]
java.lang.IllegalStateException: java.nio.file.NoSuchFileException: /var/lib/tomcat10/webapps/ehome/WEB-INF/lib/tl-xio.jar
bei org.apache.catalina.webresources.AbstractSingleArchiveResourceSet.getArchiveEntry(AbstractSingleArchiveResourceSet.java:100)
...
bei org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1186)
bei com.top_logic.basic.module.ModuleUtil.stopInThreadContext(ModuleUtil.java:759)
bei com.top_logic.basic.module.ModuleUtil.shutDown(ModuleUtil.java:736)
bei com.top_logic.basic.module.ModuleUtil$GlobalContext.shutDown(ModuleUtil.java:102)
bei com.top_logic.basic.module.ModuleUtil$ModuleContext.close(ModuleUtil.java:69)
bei com.top_logic.util.AbstractStartStopListener.shutDownModuleSystem(AbstractStartStopListener.java:650)
bei com.top_logic.util.AbstractStartStopListener.contextDestroyed(AbstractStartStopListener.java:494)
Zeile 759 ist das anonyme new InContext() {...} in stopInThreadContext, einer Klasse, die nur beim Herunterfahren geladen wird. Das Laden schlägt fehl, die Ausnahme durchbricht den gesamten Herunterfahrvorgang, und kein Modul wird gestoppt: Der Verbindungspool und das eingebettete H2 bleiben in der JVM von Tomcat offen (Threads des MVStore-Hintergrundschreibers, H2-save, Timer-n für die Eviction des commons-pools, der DefaultScheduler des Schedulers). Die nächste Bereitstellung derselben Anwendung schlägt mit einer MVStoreException fehl : Die Datei bleibt gesperrt (OverlappingFileLockException), bis Tomcat neu gestartet wird.
Das Löschen der JAR-Dateien einer laufenden Anwendung ist ein Fehler der Bereitstellung (das E-Home-Paket wartet nun auf die Deaktivierung), doch die Folge steht in keinem Verhältnis dazu: Eine Klasse, die nicht geladen werden kann, bedeutet, dass nichts heruntergefahren wird, einschließlich der Datenbank.
Erwartung
Das Herunterfahren schreitet Modul für Modul voran, selbst wenn darin etwas fehlschlägt: Die Job-Klasse von `stopInThreadContext ` (und alles andere, was der Stopp-Pfad verzögert lädt) wird beim Start mit dem Modulsystem geladen (eine beim Start referenzierte benannte Klasse oder die sofort geladene Klasse), und ein „Throwable“, das beim Beenden eines Moduls auftritt, wird protokolliert, und das Herunterfahren wird mit dem nächsten Modul fortgesetzt (derzeit fängt „BasicRuntimeModule.shutDown“ nur „Exception“ ab).
Reproduktion
Tomcat 10.1, eine Anwendung mit einer eingebetteten H2-Datenbank, die über einen Kontextdeskriptor in conf/Catalina/localhost/ bereitgestellt wird: Starten Sie das System, löschen Sie dann den Deskriptor und den entpackten Ordner `webapps/<app>/WEB-INF/lib ` innerhalb weniger Sekunden, bevor der automatische Deployer von Tomcat bemerkt, dass der Deskriptor nicht mehr vorhanden ist. Das Protokoll zeigt den oben genannten Trace, unter /proc/<tomcat>/fd ist weiterhin h2.mv.db vorhanden, und das erneute Deployment der App schlägt mit der Meldung „Die Datei ist gesperrt“ fehl.
Lösung
- Der Stopp-Pfad von „ModuleUtil“ enthält keine eigene Klasse, die ausschließlich beim Herunterfahren geladen wird: Die Aufgabe von „stopInThreadContext“ ist ein Lambda-Ausdruck, der eine Methode von „ModuleUtil“ aufruft, anstatt eine anonyme „InContext“-Klasse zu verwenden. Der umgebende Pfad „ThreadContext.inSystemContext“ ist derselbe, den bereits der Startvorgang verwendet.
- BasicRuntimeModule.shutDown protokolliert jedes „Throwable“, das beim Beenden des Dienstes auftritt (nicht nur „Exception“); das Modul ist danach inaktiv und das Herunterfahren wird mit dem nächsten Modul fortgesetzt, sodass ein fehlgeschlagenes Modul seine Abhängigkeiten (z. B. die Datenbank) nicht länger am Laufen hält.