This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch WW-5537-classloader-leak-fixes
in repository https://gitbox.apache.org/repos/asf/struts.git

commit a6b2567f19742183b190ff993735f79e0eb1aa56
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Mar 23 09:51:28 2026 +0100

    WW-5537 Add InternalDestroyable and ContextAwareDestroyable interfaces
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../dispatcher/ContextAwareDestroyable.java        | 53 ++++++++++++++++++++++
 .../struts2/dispatcher/InternalDestroyable.java    | 45 ++++++++++++++++++
 2 files changed, 98 insertions(+)

diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/ContextAwareDestroyable.java 
b/core/src/main/java/org/apache/struts2/dispatcher/ContextAwareDestroyable.java
new file mode 100644
index 000000000..bc2b4352f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/struts2/dispatcher/ContextAwareDestroyable.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.dispatcher;
+
+import jakarta.servlet.ServletContext;
+
+/**
+ * Extension of {@link InternalDestroyable} for components that require
+ * {@link ServletContext} during cleanup (e.g. clearing servlet-scoped caches).
+ *
+ * &lt;p&gt;During {@link Dispatcher#cleanup()}, the discovery loop checks each
+ * {@code InternalDestroyable} bean: if it implements this subinterface,
+ * {@link #destroy(ServletContext)} is called instead of {@link 
#destroy()}.&lt;/p&gt;
+ *
+ * @since 7.1.0
+ * @see InternalDestroyable
+ * @see Dispatcher#cleanup()
+ */
+public interface ContextAwareDestroyable extends InternalDestroyable {
+
+    /**
+     * Releases state that requires access to the {@link ServletContext}.
+     *
+     * @param servletContext the current servlet context, may be {@code null}
+     *                       if the Dispatcher was created without one
+     */
+    void destroy(ServletContext servletContext);
+
+    /**
+     * Default no-op — {@link Dispatcher} calls
+     * {@link #destroy(ServletContext)} instead when it recognises this type.
+     */
+    @Override
+    default void destroy() {
+        // no-op: context-aware variant is the real entry point
+    }
+}
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/InternalDestroyable.java 
b/core/src/main/java/org/apache/struts2/dispatcher/InternalDestroyable.java
new file mode 100644
index 000000000..997788921
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/dispatcher/InternalDestroyable.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.dispatcher;
+
+/**
+ * Internal framework interface for components that hold static state
+ * (caches, daemon threads, etc.) requiring cleanup during application
+ * undeploy to prevent classloader leaks.
+ *
+ * &lt;p&gt;Implementations are registered as named beans in {@code 
struts-beans.xml}
+ * (or plugin descriptors) with type {@code InternalDestroyable}. During
+ * {@link Dispatcher#cleanup()}, all registered implementations are discovered
+ * via {@code Container.getInstanceNames(InternalDestroyable.class)} and
+ * invoked automatically.&lt;/p&gt;
+ *
+ * &lt;p&gt;This is not part of the public user API. For user/plugin lifecycle
+ * callbacks, use {@link DispatcherListener} instead.&lt;/p&gt;
+ *
+ * @since 7.1.0
+ * @see Dispatcher#cleanup()
+ */
+public interface InternalDestroyable {
+
+    /**
+     * Releases static state held by this component. Called once during
+     * {@link Dispatcher#cleanup()}.
+     */
+    void destroy();
+}

Reply via email to