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

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 91e0187fbac HDDS-10782. Replace GSON with Jackson in EventQueue (#8949)
91e0187fbac is described below

commit 91e0187fbac0ecde2262c4e351669bb855d4eed4
Author: Anastasia Kostryukova <[email protected]>
AuthorDate: Thu Oct 9 13:20:43 2025 +0300

    HDDS-10782. Replace GSON with Jackson in EventQueue (#8949)
---
 hadoop-hdds/framework/pom.xml                      |   8 +-
 .../hadoop/hdds/server/events/EventQueue.java      | 122 +++++++++++++++++----
 2 files changed, 102 insertions(+), 28 deletions(-)

diff --git a/hadoop-hdds/framework/pom.xml b/hadoop-hdds/framework/pom.xml
index fed0f112553..5c368525010 100644
--- a/hadoop-hdds/framework/pom.xml
+++ b/hadoop-hdds/framework/pom.xml
@@ -34,6 +34,10 @@
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
@@ -50,10 +54,6 @@
       <groupId>com.github.jnr</groupId>
       <artifactId>jnr-posix</artifactId>
     </dependency>
-    <dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
-    </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
index 02778d5e89f..8ea8f6136c3 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
@@ -17,20 +17,32 @@
 
 package org.apache.hadoop.hdds.server.events;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-import com.google.gson.ExclusionStrategy;
-import com.google.gson.FieldAttributes;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.Message;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.apache.hadoop.hdds.scm.net.InnerNode;
 import org.apache.hadoop.hdds.scm.net.NodeImpl;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
@@ -59,9 +71,7 @@ public class EventQueue implements EventPublisher, 
AutoCloseable {
 
   private boolean isRunning = true;
 
-  private static final Gson TRACING_SERIALIZER = new GsonBuilder()
-          .setExclusionStrategies(new DatanodeDetailsGsonExclusionStrategy())
-          .create();
+  private static final ObjectWriter TRACING_SERIALIZER = buildSerializer();
 
   private boolean isSilent = false;
   private final String threadNamePrefix;
@@ -74,21 +84,88 @@ public EventQueue(String threadNamePrefix) {
     this.threadNamePrefix = threadNamePrefix;
   }
 
+  private static String serializeObject(Object payload) {
+    try {
+      return TRACING_SERIALIZER.writeValueAsString(payload);
+    } catch (JsonProcessingException e) {
+      return String.valueOf(payload);
+    }
+  }
+
+  private static ObjectWriter buildSerializer() {
+    ObjectMapper mapper = new ObjectMapper()
+        .enable(SerializationFeature.INDENT_OUTPUT)
+        .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
+        .addMixIn(NodeImpl.class, DatanodeDetailsJacksonMixIn.class);
+
+    SimpleModule module = new SimpleModule();
+
+    module.addSerializer(Message.class, new JsonSerializer<Message>() {
+      @Override
+      public void serialize(Message msg, JsonGenerator gen, SerializerProvider 
sp) throws IOException {
+        gen.writeObject(convertMessageToMap(msg));
+      }
+
+      private Object convertMessageToMap(Message msg) {
+        Map<String, Object> fieldMap = new LinkedHashMap<>();
+        for (Map.Entry<Descriptors.FieldDescriptor, Object> e : 
msg.getAllFields().entrySet()) {
+          String fieldName = e.getKey().getName();
+          Object value = convertField(e.getKey(), e.getValue());
+          fieldMap.put(fieldName, value);
+        }
+        return fieldMap;
+      }
+
+      /**
+       * Handles protobuf message fields.
+       */
+      private Object convertField(Descriptors.FieldDescriptor fd, Object 
value) {
+        if (fd.isRepeated()) {
+          List<?> fields = (List<?>) value;
+          List<Object> result = new ArrayList<>();
+          for (Object field : fields) {
+            result.add(convertSingleValue(fd, field));
+          }
+          return result;
+        }
+        return convertSingleValue(fd, value);
+      }
+
+      /**
+       * Converts a single field value to a JSON representation.
+       */
+      private Object convertSingleValue(Descriptors.FieldDescriptor fd, Object 
field) {
+        switch (fd.getJavaType()) {
+        case STRING:
+        case INT:
+        case LONG:
+        case FLOAT:
+        case DOUBLE:
+        case BOOLEAN:
+          return field;
+        case ENUM:
+          return ((Descriptors.EnumValueDescriptor) field).getName();
+        case BYTE_STRING:
+          ByteString bs = (ByteString) field;
+          return Base64.getEncoder().encodeToString(bs.toByteArray());
+        case MESSAGE:
+          return convertMessageToMap((Message) field);
+        default:
+          return String.valueOf(field);
+        }
+      }
+    });
+
+    mapper.registerModule(module);
+    return mapper.writerWithDefaultPrettyPrinter();
+  }
+
   // The field parent in DatanodeDetails class has the circular reference
   // which will result in Gson infinite recursive parsing. We need to exclude
   // this field when generating json string for DatanodeDetails object
-  static class DatanodeDetailsGsonExclusionStrategy
-          implements ExclusionStrategy {
-    @Override
-    public boolean shouldSkipField(FieldAttributes f) {
-      return f.getDeclaringClass() == NodeImpl.class
-              && f.getName().equals("parent");
-    }
-
-    @Override
-    public boolean shouldSkipClass(Class<?> aClass) {
-      return false;
-    }
+  abstract static class DatanodeDetailsJacksonMixIn {
+    @JsonIgnore
+    abstract InnerNode getParent();
   }
 
   /**
@@ -196,18 +273,17 @@ public <PAYLOAD, EVENT_TYPE extends Event<PAYLOAD>> void 
fireEvent(
 
     eventCount.incrementAndGet();
     if (eventExecutorListMap != null) {
-
       for (Map.Entry<EventExecutor, List<EventHandler>> executorAndHandlers :
           eventExecutorListMap.entrySet()) {
-
         for (EventHandler handler : executorAndHandlers.getValue()) {
           queuedCount.incrementAndGet();
           if (LOG.isTraceEnabled()) {
+            String jsonPayload = serializeObject(payload);
             LOG.trace(
                 "Delivering [event={}] to executor/handler {}: 
<json>{}</json>",
                 event.getName(),
                 executorAndHandlers.getKey().getName(),
-                TRACING_SERIALIZER.toJson(payload).replaceAll("\n", "\\\\n"));
+                jsonPayload.replaceAll("\n", "\\\\n"));
           } else if (LOG.isDebugEnabled()) {
             LOG.debug("Delivering [event={}] to executor/handler {}: {}",
                 event.getName(),
@@ -216,10 +292,8 @@ public <PAYLOAD, EVENT_TYPE extends Event<PAYLOAD>> void 
fireEvent(
           }
           executorAndHandlers.getKey()
               .onMessage(handler, payload, this);
-
         }
       }
-
     } else {
       if (!isSilent) {
         LOG.warn("No event handler registered for event {}", event);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to