You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ra...@apache.org on 2026/10/02 04:08:57 UTC

(logging-log4j2) 06/07: Simplify the serialization of `ObjectArrayMessage` and `ParameterizedMessage` (#4274)

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

ramanathan1504 pushed a commit to branch port/serialization-to-2.26.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 7e6eeced03f13b015aa88ce4439956401b712ab3
Author: Piotr P. Karwasz <pk...@apache.org>
AuthorDate: Wed Sep 30 23:07:06 2026 +0200

    Simplify the serialization of `ObjectArrayMessage` and `ParameterizedMessage` (#4274)
    
    * Serialize message arguments with the array helpers
    
    `ObjectArrayMessage` and `ParameterizedMessage` now write their
    arguments as a single `byte[][]` array, built with
    `SerializationUtil.wrapObjects`, instead of a length followed by the
    wrapped arguments. Each argument is serialized separately, so that
    arguments that cannot be serialized are replaced with their string
    representation, and the arrays are allocated by the JDK.
    
    This also fixes the serialization of a `ParameterizedMessage` with
    `null` arguments, which threw a `NullPointerException`.
    
    The serialized forms are incompatible with previous versions, so the
    `serialVersionUID`s are changed.
    
    Assisted-By: Claude Opus 5.5 <no...@anthropic.com>
    
    * Update src/changelog/.2.x.x/4274_wrap_object_array_message_serialization.xml
    
    ---------
    
    Co-authored-by: Ramanathan <ra...@apache.org>
    (cherry picked from commit 6a4d9c2ab01195848d401b0486eb933012d7515f)
---
 .../logging/log4j/message/ObjectArrayMessageTest.java     | 15 +++++++++++++++
 .../logging/log4j/message/ParameterizedMessageTest.java   |  8 ++++++++
 .../apache/logging/log4j/message/ObjectArrayMessage.java  |  7 ++++---
 .../logging/log4j/message/ParameterizedMessage.java       | 13 +++----------
 .../4274_wrap_object_array_message_serialization.xml      | 12 ++++++++++++
 5 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
index cdfb2c9bd2..b17b83c970 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.message;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
+import org.apache.logging.log4j.test.junit.SerialUtil;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -38,4 +39,18 @@ class ObjectArrayMessageTest {
     void testGetThrowable() {
         assertNull(OBJECT_ARRAY_MESSAGE.getThrowable());
     }
+
+    @Test
+    void testNonSerializableElementIsReplacedByItsStringForm() {
+        final ObjectArrayMessage original = new ObjectArrayMessage("A", new NonSerializable(), "C");
+        final ObjectArrayMessage restored = SerialUtil.deserialize(SerialUtil.serialize(original));
+        assertArrayEquals(new Object[] {"A", "non-serializable", "C"}, restored.getParameters());
+    }
+
+    private static final class NonSerializable {
+        @Override
+        public String toString() {
+            return "non-serializable";
+        }
+    }
 }
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
index 6ebe97d30c..eff35a24a9 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
@@ -187,6 +187,14 @@ class ParameterizedMessageTest {
         assertThat(actual.getFormattedMessage()).isEqualTo(expected.getFormattedMessage());
     }
 
+    @Test
+    void testSerializableWithNullArguments() {
+        final Message expected = new ParameterizedMessage("Hello!", (Object[]) null, null);
+        final Message actual = SerialUtil.deserialize(SerialUtil.serialize(expected));
+        assertThat(actual.getParameters()).isNull();
+        assertThat(actual.getFormattedMessage()).isEqualTo("Hello!");
+    }
+
     /**
      * In this test cases, constructed the following scenarios: <br>
      * <p>
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
index ffd83974b0..c9bf96a27c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
@@ -21,6 +21,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.Arrays;
 import org.apache.logging.log4j.util.Constants;
+import org.apache.logging.log4j.util.internal.SerializationUtil;
 
 /**
  * Handles messages that contain an Object[].
@@ -35,7 +36,7 @@ import org.apache.logging.log4j.util.Constants;
  */
 public final class ObjectArrayMessage implements Message {
 
-    private static final long serialVersionUID = -5903272448334166185L;
+    private static final long serialVersionUID = 3470900984521271892L;
 
     private transient Object[] array;
     private transient String arrayString;
@@ -118,7 +119,7 @@ public final class ObjectArrayMessage implements Message {
 
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
-        array = (Object[]) in.readObject();
+        array = SerializationUtil.unwrapObjects(in, (byte[][]) in.readObject());
     }
 
     @Override
@@ -128,6 +129,6 @@ public final class ObjectArrayMessage implements Message {
 
     private void writeObject(final ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
-        out.writeObject(array);
+        out.writeObject(SerializationUtil.wrapObjects(array));
     }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
index c054b4d376..d460c8caf3 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
@@ -76,7 +76,7 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
      */
     public static final String ERROR_SUFFIX = ParameterFormatter.ERROR_SUFFIX;
 
-    private static final long serialVersionUID = -665975803997290697L;
+    private static final long serialVersionUID = -4633763537142017035L;
 
     private static final ThreadLocal<FormatBufferHolder> FORMAT_BUFFER_HOLDER_REF =
             Constants.ENABLE_THREADLOCALS ? ThreadLocal.withInitial(FormatBufferHolder::new) : null;
@@ -373,18 +373,11 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
 
     private void writeObject(final ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
-        out.writeInt(args.length);
-        for (final Object arg : args) {
-            SerializationUtil.writeWrappedObject(arg, () -> String.valueOf(arg), out);
-        }
+        out.writeObject(SerializationUtil.wrapObjects(args));
     }
 
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
-        final int argCount = in.readInt();
-        args = new Object[argCount];
-        for (int argIndex = 0; argIndex < args.length; argIndex++) {
-            args[argIndex] = SerializationUtil.readWrappedObject(in);
-        }
+        args = SerializationUtil.unwrapObjects(in, (byte[][]) in.readObject());
     }
 }
diff --git a/src/changelog/.2.x.x/4274_wrap_object_array_message_serialization.xml b/src/changelog/.2.x.x/4274_wrap_object_array_message_serialization.xml
new file mode 100644
index 0000000000..4a925b6b05
--- /dev/null
+++ b/src/changelog/.2.x.x/4274_wrap_object_array_message_serialization.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+       type="changed">
+  <issue id="4274" link="https://github.com/apache/logging-log4j2/pull/4274"/>
+  <description format="asciidoc">
+    Replace `ObjectArrayMessage` and `ParameterizedMessage` arguments that cannot be serialized with their string form.
+  </description>
+</entry>