major
#29746
FileManagerOverlay/FileManagerDelegate cannot read resources inside web-fragment WARs (tl_config overlay fails on engine configuration files)
Scenario
An application is started (development start, com.top_logic.ide.jetty.Bootstrap) with a configuration overlay -Dtl_config=<folder>, whose metaConf.txt names a configuration file of an engine module, e.g.
ldapConf.xml
ldapConf.xml is not part of the default metaConf.txt; it lives in the web fragment of tl-core (tl-core-<version>-web-fragment.war, WEB-INF/conf/ldapConf.xml).
Startup fails:
java.io.FileNotFoundException: Could not resolve '/WEB-INF/conf/ldapConf.xml' to an existing resource. at com.top_logic.basic.FileManager.failNotExists(FileManager.java:447) at com.top_logic.basic.FileManager.getStream(FileManager.java:124) at com.top_logic.basic.FileManager$FileManagerBinaryContent.getStream(FileManager.java:602) at com.top_logic.basic.XMLProperties.loadProperties(XMLProperties.java:367) at com.top_logic.basic.XMLProperties.<init>(XMLProperties.java:351) at com.top_logic.basic.MultiProperties.<init>(MultiProperties.java:76) at com.top_logic.basic.MultiProperties.createNewXMLProperties(MultiProperties.java:158) at com.top_logic.basic.XMLProperties$Module.newImplementationInstance(XMLProperties.java:784)
The same overlay naming a file of the application's own src/main/webapp/WEB-INF/conf is resolved (verified with a file outside the default metaConf.txt). Listing ldapConf.xml in the application's own metaConf.txt (or in a deploy aspect) works as well.
Analysis
MultiProperties.createConfigResolver(configDir) resolves the overlay entries with
new FileManagerOverlay(new DefaultFileManager(configDir) {...}, FileManager.getInstance())
XMLPropertiesConfig.addConfiguration takes resolver.getData(name), a lazy FileManagerBinaryContent bound to that resolver; its stream is opened later in XMLProperties.loadProperties.
Regular resolution (MultiFileManager.getStreamOrNull): resolveFile(name, true) returns the path, then Files.newInputStream(path). Web fragments are mounted as zip file systems (PathInfo: FileSystems.newFileSystem(fragmentWar.toPath())), so the path points into tl-core-...-web-fragment.war and is read directly from the archive. This works.
Resolution through the overlay:
- FileManagerOverlay.getStreamOrNull: the overlay directory does not contain the file, so it calls super.getStreamOrNull.
- FileManagerDelegate delegates only getIDEPaths, getPaths, getDataOverlays, getResourcePaths, getFiles, getDataOrNull, delete and getIDEFile. It does not delegate getStreamOrNull and getResourceUrl, so the base implementation FileManager.getStreamOrNull runs: getResourceUrl(name), else getClasspathResourceAsStream(name).
- FileManagerOverlay.getResourceUrl calls super.getResourceUrl, i.e. the base FileManager.getResourceUrl, which uses getIDEFileOrNull(name) and thereby getIDEFile, delegated to MultiFileManager.getIDEFile.
- MultiFileManager.getIDEFile finds the path with resolveFile(name, false), but since a path inside a fragment WAR is not in the default file system (inDefaultFileSystem(path) is false), it returns inTopLevelWebapp(name), a non-existing file. getIDEFileOrNull returns null, so does getResourceUrl.
- getClasspathResourceAsStream looks up /META-INF/resources/WEB-INF/conf/ldapConf.xml on the class path, where the fragment resources are not present, and returns null.
A resource of the application's own src/main/webapp is found because it lies in the default file system, so getIDEFile returns the existing file.
The defect is not specific to tl_config: every FileManagerOverlay/`FileManagerDelegate` reads resources through getIDEFile instead of the delegate's own stream/URL resolution and therefore cannot read resources that exist only inside a web-fragment WAR.
Analysed on tl-engine 8.0.0-alpha9.
Lösung
FileManagerDelegate delegiert zusätzlich getStreamOrNull(String) und getResourceUrl(String) an den zugrunde liegenden FileManager. FileManagerOverlay fragt zuerst sein Overlay und fällt dann über super auf diese Delegation zurück, sodass die Auflösung bei MultiFileManager.getStreamOrNull/`getResourceUrl` ankommt, die Ressourcen auch aus Web-Fragment-WARs liest. Damit kann ein tl_config-Overlay Konfigurationsdateien von Engine-Modulen (z.B. ldapConf.xml aus tl-core) referenzieren.
WebappFileManager überschreibt beide Methoden selbst und ist nicht betroffen.
Abgesichert durch einen Unit-Test, der einen FileManagerOverlay über einem MultiFileManager mit einem als Zip-Dateisystem eingebundenen Archiv auflöst.