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). + * + * <p>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()}.</p> + * + * @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. + * + * <p>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.</p> + * + * <p>This is not part of the public user API. For user/plugin lifecycle + * callbacks, use {@link DispatcherListener} instead.</p> + * + * @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(); +}
