* WW-5537 Add InternalDestroyable and ContextAwareDestroyable interfaces Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 ContainerHolder: ThreadLocal with AtomicLong generation counter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 FinalizableReferenceQueue: volatile instance, join, classloader null Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 ScopeInterceptor.clearLocks: add synchronized block Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 CompoundRootAccessor, DefaultFileManager: implement InternalDestroyable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Add InternalDestroyable adapter classes for static cache cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Register InternalDestroyable beans in struts-beans.xml Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 JSON plugin: add JSONCacheDestroyable for BeanInfo cache cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Dispatcher.cleanup: refactor into focused methods with InternalDestroyable discovery Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Rewrite DispatcherCleanupTest for InternalDestroyable discovery Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Add log4j-web for proper Log4j2 lifecycle in Servlet container Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Dispatcher.destroyObjectFactory: add early return on null, use pattern matching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Fix @since annotations: 7.1.0 -> 7.2.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Add Container.destroy() to clear internal caches on undeploy Container now exposes a destroy() method that clears factories, injectors, constructors, and ThreadLocals. This releases Class<?> keys and JDK DelegatingClassLoader instances that pin the webapp classloader. DefaultConfiguration.destroy() calls container.destroy() and reloadContainer() delegates to destroy() to avoid duplication. Also fixes JSONCacheDestroyable referencing non-existent DefaultJSONWriter (renamed to StrutsJSONWriter). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Fix Container.destroy(): don't clear factories, don't call from reloadContainer factories must remain intact because existing code holds direct references to the Container after destroyConfiguration() and expects it to still resolve dependencies (e.g. during configuration reload). reloadContainer() reverted to clearing packageContexts/loadedFileNames directly — calling destroy() there nulled the container reference and cleared state needed during the bootstrap transition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Restore destroy() call in reloadContainer() The test failures were caused by factories.clear() in Container.destroy(), not by calling destroy() from reloadContainer(). Now that factories.clear() is removed, destroy() is safe to call here — it clears packageContexts, loadedFileNames, and the container's reflection caches in one place. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WW-5537 Fix Sonar issues: thread-safe FinalizableReferenceQueue, empty method comments - Replace volatile field with AtomicReference in FinalizableReferenceQueue for proper thread safety using getAndSet() - Add comments to empty destroy() implementations in test mocks - Replace deprecated new URL() with URI.toURL() in DispatcherCleanupTest - Add comments to empty listener methods in DispatcherCleanupTest Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Showcase
WARNING: This application is a demonstration/development tool only. It is NOT intended for production deployment. It contains features such as source code viewing that intentionally expose internal application details. Deploying this application on a publicly accessible server may pose security risks.
Showcase is a collection of examples with code that you might adopt and adapt in your own applications.
For more on getting started with Struts, see:
I18N
Please note that this project was created with the assumption that it will be run in an environment where the default
locale is set to English. This means that the default messages defined in package.properties are in English.
If the default locale for your server is different, then rename package.properties to package_en.properties and
create a new package.properties with proper values for your default locale.