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

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new 11c10ee8f WW-5620 Standardize logging on Log4j2 (#1794)
11c10ee8f is described below

commit 11c10ee8f9259240285369c6feaae1fe6c06fc0d
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Jul 20 13:41:45 2026 +0200

    WW-5620 Standardize logging on Log4j2 (#1794)
    
    * WW-5620 docs: add Log4j2 logging standardization design spec
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * WW-5620 docs: add Log4j2 logging standardization implementation plan
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * WW-5620 Migrate FinalizableReferenceQueue to Log4j2
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * WW-5620 Migrate AbstractDefaultToStringRenderable to Log4j2
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * WW-5620 Remove unused injectable j.u.l.Logger DI factory from 
ContainerBuilder
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * WW-5620 Remove dead first-party SLF4J dependency declarations
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 core/pom.xml                                       |  12 -
 .../apache/struts2/inject/ContainerBuilder.java    |  20 --
 .../inject/util/FinalizableReferenceQueue.java     |   9 +-
 ...07-20-WW-5620-log4j2-logging-standardization.md | 345 +++++++++++++++++++++
 ...W-5620-log4j2-logging-standardization-design.md | 135 ++++++++
 parent/pom.xml                                     |  11 -
 .../AbstractDefaultToStringRenderable.java         |   6 +-
 pom.xml                                            |   1 -
 8 files changed, 487 insertions(+), 52 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index b5303545f..6509b6a42 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -316,18 +316,6 @@
             <scope>test</scope>
         </dependency>
 
-        <!-- SLF4J support -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</artifactId>
-            <scope>test</scope>
-        </dependency>
-
         <dependency>
             <groupId>commons-validator</groupId>
             <artifactId>commons-validator</artifactId>
diff --git a/core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java 
b/core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java
index 3d5f7f6b4..0d7bc6b4e 100644
--- a/core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java
+++ b/core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java
@@ -16,14 +16,12 @@
 
 package org.apache.struts2.inject;
 
-import java.lang.reflect.Member;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.logging.Logger;
 
 /**
  * Builds a dependency injection {@link Container}. The combination of
@@ -34,7 +32,6 @@ import java.util.logging.Logger;
  *
  * <ul>
  *   <li>Injects the current {@link Container}.
- *   <li>Injects the {@link Logger} for the injected member's declaring class.
  * </ul>
  *
  * @author [email protected] (Bob Lee)
@@ -60,29 +57,12 @@ public final class ContainerBuilder {
                 }
             };
 
-    private static final InternalFactory<Logger> LOGGER_FACTORY =
-            new InternalFactory<>() {
-                public Logger create(InternalContext context) {
-                    Member member = context.getExternalContext().getMember();
-                    return member == null ? Logger.getAnonymousLogger()
-                            : 
Logger.getLogger(member.getDeclaringClass().getName());
-                }
-
-                @Override
-                public Class<? extends Logger> type() {
-                    return Logger.class;
-                }
-            };
-
     /**
      * Constructs a new builder.
      */
     public ContainerBuilder() {
         // In the current container as the default Container implementation.
         factories.put(Key.newInstance(Container.class, 
Container.DEFAULT_NAME), CONTAINER_FACTORY);
-
-        // Inject the logger for the injected member's declaring class.
-        factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME), 
LOGGER_FACTORY);
     }
 
     /**
diff --git 
a/core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java
 
b/core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java
index 07b7ececa..47027392b 100644
--- 
a/core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java
+++ 
b/core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java
@@ -19,8 +19,8 @@ package org.apache.struts2.inject.util;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 /**
  * Starts a background thread that cleans up after reclaimed referents.
@@ -29,8 +29,7 @@ import java.util.logging.Logger;
  */
 public class FinalizableReferenceQueue extends ReferenceQueue<Object> {
 
-  private static final Logger logger =
-      Logger.getLogger(FinalizableReferenceQueue.class.getName());
+  private static final Logger LOG = 
LogManager.getLogger(FinalizableReferenceQueue.class);
 
   private final AtomicReference<Thread> cleanupThread = new 
AtomicReference<>();
 
@@ -45,7 +44,7 @@ public class FinalizableReferenceQueue extends 
ReferenceQueue<Object> {
   }
 
   void deliverBadNews(Throwable t) {
-    logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
+    LOG.error("Error cleaning up after reference.", t);
   }
 
   void start() {
diff --git 
a/docs/superpowers/plans/2026-07-20-WW-5620-log4j2-logging-standardization.md 
b/docs/superpowers/plans/2026-07-20-WW-5620-log4j2-logging-standardization.md
new file mode 100644
index 000000000..87442e449
--- /dev/null
+++ 
b/docs/superpowers/plans/2026-07-20-WW-5620-log4j2-logging-standardization.md
@@ -0,0 +1,345 @@
+# WW-5620 Log4j2 Logging Standardization Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use 
superpowers:subagent-driven-development (recommended) or 
superpowers:executing-plans to implement this plan task-by-task. Steps use 
checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Migrate the three remaining legacy-logging source files to Log4j2 
and remove the now-dead first-party SLF4J dependency declarations.
+
+**Architecture:** Three independent, mostly mechanical source edits (two 
class-own logger swaps, one dead-DI-feature removal), followed by a pom cleanup 
gated on a `mvn dependency:tree` check. No functional or security behavior 
changes.
+
+**Tech Stack:** Java, Log4j2 (`org.apache.logging.log4j`), Maven. Core tests 
are JUnit 4 + AssertJ + Mockito in this repo.
+
+## Global Constraints
+
+- Fix version: 7.3.0. Target ticket: WW-5620. Every commit message MUST be 
prefixed `WW-5620`.
+- Logger convention: `private static final Logger LOG = 
LogManager.getLogger(<Class>.class);` for static class loggers (see 
`ActionSupport`, `ObjectFactory`). Preserve the existing field style where a 
file already uses an instance logger.
+- No new pom dependency may be added. `log4j-api` is already available 
(directly in core, transitively in tiles via `struts2-core`).
+- Do NOT touch the runtime bridges `log4j-jcl`, `log4j-slf4j-impl`, or 
`commons-logging` — they route third-party logging and are out of scope.
+- Build/test commands: `mvn test -DskipAssembly -pl core` and `mvn test 
-DskipAssembly -pl plugins/tiles`.
+- Reference spec: 
`docs/superpowers/specs/2026-07-20-WW-5620-log4j2-logging-standardization-design.md`.
+
+---
+
+## File Structure
+
+- Modify: 
`core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java`
 — swap `java.util.logging` → Log4j2.
+- Modify: 
`plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java`
 — swap SLF4J → Log4j2.
+- Modify: `core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java` 
— remove the injectable `java.util.logging.Logger` DI factory.
+- Modify: `core/pom.xml`, `parent/pom.xml`, `pom.xml` — remove dead 
first-party SLF4J declarations.
+
+No test files are created: files 1–2 are behavior-preserving; file 3 removes 
dead, untested code. Verification is via existing suites, `dependency:tree`, 
and grep.
+
+---
+
+### Task 1: Migrate `FinalizableReferenceQueue` to Log4j2
+
+**Files:**
+- Modify: 
`core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java`
+
+**Interfaces:**
+- Consumes: nothing from other tasks.
+- Produces: nothing consumed by other tasks (internal logger only).
+
+- [ ] **Step 1: Confirm current state**
+
+Run: `grep -n "logging\|logger\|Level" 
core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java`
+Expected: lines showing `import java.util.logging.Level;`, `import 
java.util.logging.Logger;`, the `logger` field 
(`Logger.getLogger(...getName())`), and `logger.log(Level.SEVERE, "Error 
cleaning up after reference.", t);`.
+
+- [ ] **Step 2: Replace the imports**
+
+Replace:
+```java
+import java.util.logging.Level;
+import java.util.logging.Logger;
+```
+with:
+```java
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+```
+(Keep these in import-order position consistent with the file; the surrounding 
`java.lang.ref.*` / `java.util.concurrent.*` imports stay.)
+
+- [ ] **Step 3: Replace the logger field**
+
+Replace:
+```java
+  private static final Logger logger =
+      Logger.getLogger(FinalizableReferenceQueue.class.getName());
+```
+with:
+```java
+  private static final Logger LOG = 
LogManager.getLogger(FinalizableReferenceQueue.class);
+```
+
+- [ ] **Step 4: Replace the log call**
+
+Replace:
+```java
+    logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
+```
+with:
+```java
+    LOG.error("Error cleaning up after reference.", t);
+```
+
+- [ ] **Step 5: Verify no leftover legacy references**
+
+Run: `grep -n "java.util.logging\|Level\.\|logger\b" 
core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java`
+Expected: no matches.
+
+- [ ] **Step 6: Compile core**
+
+Run: `mvn -q -o compile -pl core -DskipAssembly` (drop `-o` if the local repo 
needs downloads)
+Expected: BUILD SUCCESS.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add 
core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java
+git commit -m "WW-5620 Migrate FinalizableReferenceQueue to Log4j2
+
+Co-Authored-By: Claude Opus 4.8 <[email protected]>"
+```
+
+---
+
+### Task 2: Migrate `AbstractDefaultToStringRenderable` to Log4j2
+
+**Files:**
+- Modify: 
`plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java`
+
+**Interfaces:**
+- Consumes: nothing from other tasks.
+- Produces: nothing consumed by other tasks (internal instance logger only).
+
+- [ ] **Step 1: Confirm current state**
+
+Run: `grep -n "slf4j\|Logger\|log\." 
plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java`
+Expected: `import org.slf4j.Logger;`, `import org.slf4j.LoggerFactory;`, 
`private final Logger log = LoggerFactory.getLogger(getClass());`, and 
`log.error("Error when closing a StringWriter, the impossible happened!", e);`.
+
+- [ ] **Step 2: Replace the imports**
+
+Replace:
+```java
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+```
+with:
+```java
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+```
+(Keep import ordering consistent with the file; `org.apache.logging...` sorts 
near the other `org.apache.*` imports.)
+
+- [ ] **Step 3: Replace the logger field factory call**
+
+Replace:
+```java
+    private final Logger log = LoggerFactory.getLogger(getClass());
+```
+with:
+```java
+    private final Logger log = LogManager.getLogger(getClass());
+```
+(The `log.error(...)` call is unchanged — the Log4j2 `Logger.error(String, 
Throwable)` signature is call-compatible.)
+
+- [ ] **Step 4: Verify no leftover SLF4J references**
+
+Run: `grep -n "slf4j\|LoggerFactory" 
plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java`
+Expected: no matches.
+
+- [ ] **Step 5: Compile the tiles plugin (confirms log4j-api resolves 
transitively)**
+
+Run: `mvn -q -o compile -pl plugins/tiles -am -DskipAssembly` (drop `-o` if 
downloads are needed)
+Expected: BUILD SUCCESS. If it fails with `package org.apache.logging.log4j 
does not exist`, STOP — the transitive assumption is wrong; report back before 
adding any dependency.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add 
plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java
+git commit -m "WW-5620 Migrate AbstractDefaultToStringRenderable to Log4j2
+
+Co-Authored-By: Claude Opus 4.8 <[email protected]>"
+```
+
+---
+
+### Task 3: Remove the injectable Logger factory from `ContainerBuilder`
+
+**Files:**
+- Modify: `core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java`
+
+**Interfaces:**
+- Consumes: nothing from other tasks.
+- Produces: removes the DI binding for `java.util.logging.Logger` (verified 
unused repo-wide).
+
+- [ ] **Step 1: Re-confirm the injectable Logger is unused**
+
+Run: `grep -rn --include="*.java" "java.util.logging.Logger" . | grep -v 
/target/ | grep -v "ContainerBuilder.java\|FinalizableReferenceQueue.java"`
+Expected: no matches (after Task 1, `FinalizableReferenceQueue` no longer 
appears either). If any consumer appears, STOP and report.
+
+- [ ] **Step 2: Remove the `LOGGER_FACTORY` constant**
+
+Delete this block:
+```java
+    private static final InternalFactory<Logger> LOGGER_FACTORY =
+            new InternalFactory<>() {
+                public Logger create(InternalContext context) {
+                    Member member = context.getExternalContext().getMember();
+                    return member == null ? Logger.getAnonymousLogger()
+                            : 
Logger.getLogger(member.getDeclaringClass().getName());
+                }
+
+                @Override
+                public Class<? extends Logger> type() {
+                    return Logger.class;
+                }
+            };
+```
+
+- [ ] **Step 3: Remove the factory registration in the constructor**
+
+Delete these lines (comment + registration):
+```java
+        // Inject the logger for the injected member's declaring class.
+        factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME), 
LOGGER_FACTORY);
+```
+Leave the preceding `CONTAINER_FACTORY` registration intact.
+
+- [ ] **Step 4: Remove the now-unused imports**
+
+Delete:
+```java
+import java.lang.reflect.Member;
+```
+and
+```java
+import java.util.logging.Logger;
+```
+
+- [ ] **Step 5: Update the class Javadoc**
+
+In the class Javadoc `<ul>`, delete the line:
+```java
+ *   <li>Injects the {@link Logger} for the injected member's declaring class.
+```
+Leave the `<li>Injects the current {@link Container}.` bullet.
+
+- [ ] **Step 6: Verify no leftover references**
+
+Run: `grep -n "Logger\|Member\|LOGGER_FACTORY" 
core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java`
+Expected: no matches.
+
+- [ ] **Step 7: Compile core**
+
+Run: `mvn -q -o compile -pl core -DskipAssembly` (drop `-o` if downloads are 
needed)
+Expected: BUILD SUCCESS.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java
+git commit -m "WW-5620 Remove unused injectable j.u.l.Logger DI factory from 
ContainerBuilder
+
+Co-Authored-By: Claude Opus 4.8 <[email protected]>"
+```
+
+---
+
+### Task 4: Remove dead first-party SLF4J dependency declarations
+
+**Files:**
+- Modify: `core/pom.xml`
+- Modify: `parent/pom.xml`
+- Modify: `pom.xml`
+
+**Interfaces:**
+- Consumes: Tasks 1–3 must be done first (no first-party source uses SLF4J 
anymore).
+- Produces: nothing consumed by other tasks.
+
+- [ ] **Step 1: Dependency-tree gate (before deleting anything)**
+
+Run: `grep -rln --include="*.java" "org.slf4j" . | grep -v /target/`
+Expected: no matches (Task 2 removed the last usage).
+
+Run: `mvn -q -o dependency:tree -pl core,plugins/tiles -Dincludes=org.slf4j 
2>&1 | grep -i slf4j` (drop `-o` if downloads are needed)
+Expected: any `org.slf4j:*` shown is transitive (an indented child of another 
artifact), not a direct first-party declaration. If a first-party module 
*requires* Struts to expose SLF4J for compilation, STOP, keep the minimum 
needed, and note it in the spec's Verification section before continuing.
+
+- [ ] **Step 2: Remove the SLF4J block from `core/pom.xml`**
+
+Delete:
+```xml
+        <!-- SLF4J support -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+```
+
+- [ ] **Step 3: Remove the SLF4J `dependencyManagement` entries from 
`parent/pom.xml`**
+
+Delete:
+```xml
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+```
+
+- [ ] **Step 4: Remove the `slf4j.version` property from root `pom.xml`**
+
+Delete:
+```xml
+        <slf4j.version>2.0.18</slf4j.version>
+```
+
+- [ ] **Step 5: Verify no SLF4J declarations remain**
+
+Run: `grep -rn "slf4j" --include="pom.xml" . | grep -v /target/`
+Expected: only bridge artifacts remain — `apps/showcase` `log4j-slf4j-impl` 
(and its `<name>slf4j</name>` profile entry). No `org.slf4j:slf4j-api`, no 
`slf4j-simple`, no `slf4j.version`.
+
+- [ ] **Step 6: Build and test core + tiles**
+
+Run: `mvn -o test -DskipAssembly -pl core -am` (drop `-o` if downloads are 
needed)
+Expected: BUILD SUCCESS, tests green.
+
+Run: `mvn -o test -DskipAssembly -pl plugins/tiles -am`
+Expected: BUILD SUCCESS, tests green.
+
+If any test fails because it silently relied on `slf4j-simple` as a test 
logging backend, STOP and report — do not add unrelated logging config to force 
a pass.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add core/pom.xml parent/pom.xml pom.xml
+git commit -m "WW-5620 Remove dead first-party SLF4J dependency declarations
+
+Co-Authored-By: Claude Opus 4.8 <[email protected]>"
+```
+
+---
+
+## Self-Review
+
+**Spec coverage:**
+- Spec §1 FinalizableReferenceQueue → Task 1. ✓
+- Spec §2 AbstractDefaultToStringRenderable → Task 2. ✓
+- Spec §3 ContainerBuilder factory removal → Task 3. ✓
+- Spec §4 dependency cleanup (core/parent/root poms) → Task 4. ✓
+- Spec Verification (dependency-tree gate, compile+test, grep) → Task 4 Step 
1, per-task compile steps, grep steps. ✓
+- Out-of-scope bridges left untouched → asserted in Global Constraints and 
Task 4 Step 5. ✓
+
+**Placeholder scan:** No TBD/TODO/"handle edge cases"/vague steps. Every code 
step shows exact before/after. ✓
+
+**Type consistency:** Logger field named `LOG` (static, Task 1) vs `log` 
(instance, Task 2) — intentional, matches each file's existing style and the 
codebase convention; no cross-task symbol sharing. ✓
diff --git 
a/docs/superpowers/specs/2026-07-20-WW-5620-log4j2-logging-standardization-design.md
 
b/docs/superpowers/specs/2026-07-20-WW-5620-log4j2-logging-standardization-design.md
new file mode 100644
index 000000000..db5b619ef
--- /dev/null
+++ 
b/docs/superpowers/specs/2026-07-20-WW-5620-log4j2-logging-standardization-design.md
@@ -0,0 +1,135 @@
+# WW-5620 — Standardize logging on Log4j2
+
+- **Jira:** [WW-5620](https://issues.apache.org/jira/browse/WW-5620)
+- **Type:** Improvement (refactor / dependency cleanup)
+- **Fix version:** 7.3.0
+- **Components:** Core, Plugin - Tiles
+- **Date:** 2026-07-20
+
+## Goal
+
+Log4j2 is the project's standard logging layer. Three source files still use
+legacy logging frameworks. Migrate them to Log4j2 and remove the now-dead
+first-party SLF4J dependency declarations that only existed to support them.
+
+This is a pure logging-standardization refactor: **no functional or security
+behavior changes.** All three files are vendored (files 1 and 3 from Google
+Guice, file 2 from Apache Tiles), so there is no upstream-sync concern.
+
+## Scope
+
+### In scope
+1. Migrate `FinalizableReferenceQueue.java` (core) off `java.util.logging`.
+2. Migrate `AbstractDefaultToStringRenderable.java` (tiles) off SLF4J.
+3. Remove the injectable `java.util.logging.Logger` DI factory from
+   `ContainerBuilder.java` (core).
+4. Remove the first-party SLF4J dependency declarations that become dead after
+   step 2, gated on a build/dependency-tree verification.
+
+### Out of scope
+- The commons-logging → Log4j2 and SLF4J → Log4j2 **runtime bridges**
+  (`log4j-jcl`, `log4j-slf4j-impl`, `commons-logging`). These route
+  *transitive third-party* library logging into Log4j2 and are unrelated to
+  first-party code. They stay.
+- Any broader logging refactor beyond these three files.
+
+## File-level changes
+
+### 1. 
`core/src/main/java/org/apache/struts2/inject/util/FinalizableReferenceQueue.java`
+
+Faithful class-own logger swap, adopting the codebase convention (`private
+static final Logger LOG = LogManager.getLogger(<Class>.class);` — see
+`ActionSupport`, `ObjectFactory`, `DefaultActionInvocation`).
+
+- Remove imports `java.util.logging.Level`, `java.util.logging.Logger`.
+- Add imports `org.apache.logging.log4j.LogManager`,
+  `org.apache.logging.log4j.Logger`.
+- Replace
+  `private static final Logger logger = 
Logger.getLogger(FinalizableReferenceQueue.class.getName());`
+  with
+  `private static final Logger LOG = 
LogManager.getLogger(FinalizableReferenceQueue.class);`
+- Replace
+  `logger.log(Level.SEVERE, "Error cleaning up after reference.", t);`
+  with
+  `LOG.error("Error cleaning up after reference.", t);`
+
+### 2. 
`plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java`
+
+Faithful SLF4J swap. The Log4j2 `Logger` error API is call-compatible, so only
+imports and the factory call change.
+
+- Remove imports `org.slf4j.Logger`, `org.slf4j.LoggerFactory`.
+- Add imports `org.apache.logging.log4j.LogManager`,
+  `org.apache.logging.log4j.Logger`.
+- Replace `private final Logger log = LoggerFactory.getLogger(getClass());`
+  with `private final Logger log = LogManager.getLogger(getClass());`
+- `log.error("Error when closing a StringWriter, the impossible happened!", 
e);`
+  is unchanged.
+- No new pom dependency: `log4j-api` reaches the tiles plugin transitively via
+  `struts2-core`. Confirm at build time.
+
+### 3. `core/src/main/java/org/apache/struts2/inject/ContainerBuilder.java`
+
+Here `java.util.logging.Logger` is **not** a class-own logger — it is
+registered as an injectable DI type (a legacy Guice feature: consumers could
+`@Inject` a `Logger` scoped to their declaring class). A repo-wide search found
+**no code that injects it**. Per decision, remove the dead feature rather than
+port it to Log4j2 (which has no `getAnonymousLogger()` equivalent for the
+`member == null` fallback and would change the DI contract).
+
+- Remove the `LOGGER_FACTORY` `InternalFactory<Logger>` constant.
+- Remove its registration:
+  `factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME), 
LOGGER_FACTORY);`
+  and the preceding comment.
+- Remove now-unused imports `java.util.logging.Logger` and
+  `java.lang.reflect.Member` (`Member` is used only inside the removed 
factory).
+- Update the class Javadoc: drop the
+  "Injects the `{@link Logger}` for the injected member's declaring class"
+  bullet, leaving the `Container` bullet.
+
+### 4. Dependency cleanup (gated on verification)
+
+Evidence for removal:
+- The tiles file (step 2) is the only main-source SLF4J user in the repo.
+- No test source uses SLF4J; there is no `simplelogger.properties`.
+- core's `slf4j-api` is `optional` (never part of the transitive API contract)
+  and dates to the original xwork merge.
+
+Remove, after the verification gate below:
+- `core/pom.xml` — the optional `slf4j-api` dependency and the test-scoped
+  `slf4j-simple` dependency (the `<!-- SLF4J support -->` block).
+- `parent/pom.xml` — the `dependencyManagement` entries for `slf4j-api` and
+  `slf4j-simple`.
+- root `pom.xml` — the `<slf4j.version>` property.
+
+`java.util.logging` is JDK built-in — nothing to remove for the two core files.
+
+## Verification
+
+1. **Dependency-tree gate (do before deleting pom entries):**
+   run `mvn dependency:tree` for `core` and `plugins/tiles`. Confirm no
+   first-party module *requires* Struts to expose SLF4J. If something genuinely
+   needs it, keep that minimum and record the reason here.
+2. **Compile + test:**
+   - `mvn test -DskipAssembly -pl core` (core tests are JUnit 4 in this repo)
+   - `mvn test -DskipAssembly -pl plugins/tiles`
+3. **Grep confirmation:** no source references the injectable `Logger` type,
+   the removed `logger` field, or `org.slf4j` after the change.
+
+## Testing strategy
+
+No new tests are required:
+- Files 1 and 2 are behavior-preserving logger swaps.
+- File 3 removes dead, untested code.
+
+Existing core and tiles test suites must stay green.
+
+## Risks
+
+- **Low.** The only non-mechanical change is removing the injectable `Logger`
+  factory in `ContainerBuilder`, which is unused internally. A third-party
+  extension that injected `java.util.logging.Logger` from the Struts container
+  would lose that binding; this is acceptable for the 7.3.0 minor and called 
out
+  here.
+- The dependency-tree gate protects against an unexpected transitive reliance 
on
+  the SLF4J declarations.
diff --git a/parent/pom.xml b/parent/pom.xml
index 063269182..59defe45a 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -274,17 +274,6 @@
                 <scope>test</scope>
             </dependency>
 
-            <dependency>
-                <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-api</artifactId>
-                <version>${slf4j.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-simple</artifactId>
-                <version>${slf4j.version}</version>
-            </dependency>
-
             <dependency>
                 <groupId>org.apache.logging.log4j</groupId>
                 <artifactId>log4j-bom</artifactId>
diff --git 
a/plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java
 
b/plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java
index 7498bb5d2..be211e0ef 100644
--- 
a/plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java
+++ 
b/plugins/tiles/src/main/java/org/apache/tiles/velocity/template/AbstractDefaultToStringRenderable.java
@@ -26,14 +26,14 @@ import java.util.Map;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.apache.tiles.velocity.TilesVelocityException;
 import org.apache.velocity.context.Context;
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.exception.ResourceNotFoundException;
 import org.apache.velocity.runtime.Renderable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Renderable that provides a default implementation of Renderable#toString()
@@ -74,7 +74,7 @@ public abstract class AbstractDefaultToStringRenderable 
implements Renderable {
     /**
      * The logging object.
      */
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final Logger log = LogManager.getLogger(getClass());
 
     /**
      * Constructor.
diff --git a/pom.xml b/pom.xml
index d9a188a54..d471e8e77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -125,7 +125,6 @@
         <log4j2.version>2.26.1</log4j2.version>
         <mockito.version>5.23.0</mockito.version>
         <ognl.version>3.4.11</ognl.version>
-        <slf4j.version>2.0.18</slf4j.version>
         <spring.version>6.2.12</spring.version>
         <struts-annotations.version>2.0</struts-annotations.version>
         <velocity-tools.version>3.1</velocity-tools.version>

Reply via email to