jdaugherty commented on code in PR #16741:
URL: https://github.com/apache/grails-core/pull/16741#discussion_r4361983943


##########
grails-undertow/spring-boot/src/main/java/org/apache/grails/undertow/core/servlet/UndertowServletWebServerFactory.java:
##########
@@ -219,12 +222,13 @@ public void setPreservePathOnForward(boolean 
preservePathOnForward) {
     @Override
     public WebServer getWebServer(ServletContextInitializer... initializers) {
         Builder builder = createBuilder(this, this::getSslBundle, 
this::getServerNameSslBundles);
-        DeploymentManager manager = createManager(initializers);
+        DeploymentManager manager = createManager(new TempDirs(getPort()), 
initializers);
         return getUndertowWebServer(builder, manager, getPort());
     }
 
-    private DeploymentManager createManager(ServletContextInitializer... 
initializers) {
+    private DeploymentManager createManager(TempDirs tempDirs, 
ServletContextInitializer... initializers) {
         DeploymentInfo deployment = Servlets.deployment();
+        deployment.addDeploymentCompleteListener(new 
CleanTempDirsListener(tempDirs));

Review Comment:
   Undertow runs the deployment-complete listeners' `contextDestroyed` at the 
top of `DeploymentManagerImpl.undeploy()`, before `deployment.destroy()` calls 
the application's own `ServletContextListener`s, so the directory the servlet 
context exposes as `jakarta.servlet.context.tempdir` is already gone while 
application listeners are still in `contextDestroyed`. Tomcat's 
`CleanTempDirsListener` cleans up on the server's `AFTER_DESTROY_EVENT` and 
Jetty's on `lifeCycleStopped` of the `Server`, both after the context has been 
destroyed.
   
   I probed it with a listener registered through a `ServletContextInitializer` 
that records `((File) 
servletContext.getAttribute(ServletContext.TEMPDIR)).isDirectory()` in 
`contextDestroyed`: on this branch it records `false` after `stop()`.
   
   Registering the cleanup as an ordinary listener, first in the 
`DeploymentInfo`, keeps Tomcat's and Jetty's order: 
`ApplicationListeners.contextDestroyed()` walks the listeners in reverse 
registration order, and the listeners the initializers add come after the 
`DeploymentInfo` ones, so a listener added here runs last:
   
   ```java
   deployment.addListener(new ListenerInfo(CleanTempDirsListener.class,
       new ImmediateInstanceFactory<>(new CleanTempDirsListener(tempDirs))));
   ```
   
   With that the probe records `true` and the module's tests, including the 
three new ones, pass. Could you switch to that and keep the probe as a test?



##########
grails-undertow/spring-boot/src/main/java/org/apache/grails/undertow/core/servlet/UndertowServletWebServer.java:
##########
@@ -92,4 +92,17 @@ public DeploymentManager getDeploymentManager() {
         return this.manager;
     }
 
+    /**
+     * Stops the server, which undeploys the deployment of a server that 
started, and
+     * undeploys the deployment of a server that never started, so that the 
listeners of
+     * its servlet context are destroyed and its temporary directories deleted.
+     */
+    @Override
+    public void destroy() {
+        super.destroy();
+        if (this.manager != null && this.manager.getState() == 
DeploymentManager.State.DEPLOYED) {

Review Comment:
   A deployment is also left behind when the handler chain fails to build after 
the deployment started. `DeploymentManagerHandler` starts the manager in its 
constructor, so when a later factory throws from `createHttpHandler()`, for 
instance `AccessLogHttpHandlerFactory` when the access log directory cannot be 
created, `undertow` is still null, `destroySilently()` closes nothing, and the 
manager stays `STARTED`. Boot's `ServletWebServerApplicationContext.refresh()` 
then calls `stop()` and `destroy()` on the failed server, and this guard skips 
a `STARTED` deployment, so `contextDestroyed` never runs and the directories 
stay.
   
   I probed it with `setAccessLogEnabled(true)` and an access log directory 
under a regular file: `start()` throws `WebServerException`, and after `stop()` 
and `destroy()` a registered listener's `contextDestroyed` has not run and the 
`undertow.<port>.*` directory still exists.
   
   Stopping a started deployment and undeploying whatever is not `UNDEPLOYED` 
covers both cases, and the module's tests pass with it:
   
   ```java
   try {
       if (this.manager.getState() == DeploymentManager.State.STARTED) {
           this.manager.stop();
       }
   } catch (ServletException ex) {
       throw new WebServerException("Unable to stop Undertow deployment", ex);
   }
   if (this.manager.getState() != DeploymentManager.State.UNDEPLOYED) {
       this.manager.undeploy();
   }
   ```
   
   Worth a test next to `aServerThatNeverStartedIsUndeployedWhenItIsDestroyed`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to