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

szetszwo 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 9c97ef29ab7 HDDS-14846. Implement ScmCodecFactory without reflection 
(#9952)
9c97ef29ab7 is described below

commit 9c97ef29ab7f6087eb48ed98b3467b043cc4420a
Author: Russole <[email protected]>
AuthorDate: Tue Mar 24 03:42:57 2026 +0800

    HDDS-14846. Implement ScmCodecFactory without reflection (#9952)
---
 .../apache/hadoop/hdds/scm/ha/ReflectionUtil.java  | 48 -------------
 .../hadoop/hdds/scm/ha/SCMHAManagerStub.java       | 23 +-----
 .../apache/hadoop/hdds/scm/ha/SCMRatisRequest.java | 32 ++++-----
 .../hadoop/hdds/scm/ha/SCMRatisResponse.java       | 18 ++---
 .../apache/hadoop/hdds/scm/ha/SCMStateMachine.java | 14 ++--
 .../hadoop/hdds/scm/ha/io/ScmCodecFactory.java     | 84 ++++++++++++++--------
 .../apache/hadoop/hdds/scm/ha/io/ScmListCodec.java |  7 +-
 .../hadoop/hdds/scm/ha/TestSCMRatisResponse.java   |  4 +-
 8 files changed, 92 insertions(+), 138 deletions(-)

diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ReflectionUtil.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ReflectionUtil.java
deleted file mode 100644
index e927166f2b2..00000000000
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ReflectionUtil.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.hadoop.hdds.scm.ha;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Reflection util for SCM HA.
- */
-public final class ReflectionUtil {
-
-  private static Map<String, Class<?>> classCache = new HashMap<>();
-
-  private ReflectionUtil() {
-  }
-
-  /**
-   * Returns the {@code Class} object associated with the given string name.
-   *
-   * @param className the fully qualified name of the desired class.
-   * @return the {@code Class} object for the class with the
-   *         specified name.
-   * @throws ClassNotFoundException if the class cannot be located
-   */
-  public static Class<?> getClass(String className)
-      throws ClassNotFoundException {
-    if (!classCache.containsKey(className)) {
-      classCache.put(className, Class.forName(className));
-    }
-    return classCache.get(className);
-  }
-}
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMHAManagerStub.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMHAManagerStub.java
index ea8e6dc7e9e..d9cd0a99e43 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMHAManagerStub.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMHAManagerStub.java
@@ -18,9 +18,7 @@
 package org.apache.hadoop.hdds.scm.ha;
 
 import com.google.common.base.Preconditions;
-import com.google.protobuf.InvalidProtocolBufferException;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumMap;
@@ -218,26 +216,7 @@ public boolean triggerSnapshot() throws IOException {
     }
 
     private Message process(final SCMRatisRequest request) throws Exception {
-      try {
-        final Object handler = handlers.get(request.getType());
-
-        if (handler == null) {
-          throw new IOException(
-              "No handler found for request type " + request.getType());
-        }
-
-        final Object result = handler.getClass()
-            .getMethod(request.getOperation(),
-                request.getParameterTypes())
-            .invoke(handler, request.getArguments());
-
-        return SCMRatisResponse.encode(result);
-      } catch (NoSuchMethodException | SecurityException ex) {
-        throw new InvalidProtocolBufferException(ex.getMessage());
-      } catch (InvocationTargetException e) {
-        final Throwable target = e.getTargetException();
-        throw target instanceof Exception ? (Exception) target : e;
-      }
+      return SCMStateMachine.process(request, handlers.get(request.getType()));
     }
 
     @Override
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisRequest.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisRequest.java
index 78d70cc099c..e1cd6a9a85a 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisRequest.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisRequest.java
@@ -37,6 +37,7 @@
  */
 public final class SCMRatisRequest {
 
+  static final ScmCodecFactory FACTORY =  ScmCodecFactory.getInstance();
   private final RequestType type;
   private final String operation;
   private final Object[] arguments;
@@ -96,20 +97,18 @@ public Message encode() throws 
InvalidProtocolBufferException {
     final Method.Builder methodBuilder = Method.newBuilder();
     methodBuilder.setName(operation);
 
-    final List<MethodArgument> args = new ArrayList<>();
-
-    int paramCounter = 0;
-    for (Object argument : arguments) {
-      final MethodArgument.Builder argBuilder = MethodArgument.newBuilder();
+    for (int i = 0; i < parameterTypes.length; i++) {
       // Set actual method parameter type, not actual argument type.
       // This is done to avoid MethodNotFoundException in case if argument is
       // subclass type, where as method is defined with super class type.
-      argBuilder.setType(parameterTypes[paramCounter++].getName());
-      argBuilder.setValue(ScmCodecFactory.getCodec(argument.getClass())
-          .serialize(argument));
-      args.add(argBuilder.build());
+      final Class<?> parameterType = parameterTypes[i];
+      final Class<?> resolved = FACTORY.resolve(parameterType);
+
+      methodBuilder.addArgs(MethodArgument.newBuilder()
+          .setType(parameterType.getName())
+          .setValue(FACTORY.getCodec(resolved).serialize(arguments[i]))
+          .build());
     }
-    methodBuilder.addAllArgs(args);
     requestProtoBuilder.setMethod(methodBuilder.build());
     final SCMRatisRequestProto requestProto = requestProtoBuilder.build();
     return Message.valueOf(requestProto.toByteString());
@@ -149,15 +148,10 @@ public static SCMRatisRequest decode(Message message)
       if (!argument.hasValue()) {
         throw new InvalidProtocolBufferException("Missing argument value");
       }
-      try {
-        final Class<?> clazz = ReflectionUtil.getClass(argument.getType());
-        parameterTypes[paramCounter++] = clazz;
-        args.add(ScmCodecFactory.getCodec(clazz)
-            .deserialize(argument.getValue()));
-      } catch (ClassNotFoundException ex) {
-        throw new InvalidProtocolBufferException(argument.getType() +
-            " cannot be decoded!" + ex.getMessage());
-      }
+      final Class<?> parameterType = FACTORY.getClass(argument.getType());
+      final Class<?> clazz = FACTORY.resolve(parameterType);
+      parameterTypes[paramCounter++] = parameterType;
+      args.add(FACTORY.getCodec(clazz).deserialize(argument.getValue()));
     }
     return new SCMRatisRequest(requestProto.getType(),
         method.getName(), parameterTypes, args.toArray());
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisResponse.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisResponse.java
index ddb65c46936..ac69679cd7e 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisResponse.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMRatisResponse.java
@@ -29,6 +29,7 @@
  * Represents the response from RatisServer.
  */
 public final class SCMRatisResponse {
+  static final ScmCodecFactory FACTORY =  ScmCodecFactory.getInstance();
 
   private final boolean success;
   private final Object result;
@@ -65,17 +66,18 @@ public Exception getException() {
     return exception;
   }
 
-  public static Message encode(final Object result)
+  public static Message encode(Object result, Class<?> type)
       throws InvalidProtocolBufferException {
 
     if (result == null) {
       return Message.EMPTY;
     }
 
-    final Class<?> type = result.getClass();
+    final Class<?> resolved = FACTORY.resolve(type);
+
     final SCMRatisResponseProto response = SCMRatisResponseProto.newBuilder()
         .setType(type.getName())
-        .setValue(ScmCodecFactory.getCodec(type).serialize(result))
+        .setValue(FACTORY.getCodec(resolved).serialize(result))
         .build();
     return 
Message.valueOf(UnsafeByteOperations.unsafeWrap(response.toByteString().asReadOnlyByteBuffer()));
   }
@@ -102,14 +104,8 @@ public static SCMRatisResponse decode(RaftClientReply 
reply)
       throw new InvalidProtocolBufferException("Missing response value");
     }
 
-    try {
-      final Class<?> type = ReflectionUtil.getClass(responseProto.getType());
-      return new SCMRatisResponse(ScmCodecFactory.getCodec(type)
-          .deserialize(responseProto.getValue()));
-    } catch (ClassNotFoundException e) {
-      throw new InvalidProtocolBufferException(responseProto.getType() +
-          " cannot be decoded!" + e.getMessage());
-    }
+    final Class<?> type = FACTORY.resolve(responseProto.getType());
+    return new 
SCMRatisResponse(FACTORY.getCodec(type).deserialize(responseProto.getValue()));
   }
 
 }
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java
index d702bb2a5d4..9d49ca36b6f 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java
@@ -25,6 +25,7 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.EnumMap;
 import java.util.List;
@@ -178,18 +179,19 @@ public CompletableFuture<Message> applyTransaction(
   }
 
   private Message process(final SCMRatisRequest request) throws Exception {
-    try {
-      final Object handler = handlers.get(request.getType());
+    return process(request, handlers.get(request.getType()));
+  }
 
+  public static Message process(final SCMRatisRequest request, Object handler) 
throws Exception {
+    try {
       if (handler == null) {
         throw new IOException("No handler found for request type " +
             request.getType());
       }
 
-      final Object result = handler.getClass().getMethod(
-          request.getOperation(), request.getParameterTypes())
-          .invoke(handler, request.getArguments());
-      return SCMRatisResponse.encode(result);
+      final Method method = 
handler.getClass().getMethod(request.getOperation(), 
request.getParameterTypes());
+      final Object result = method.invoke(handler, request.getArguments());
+      return SCMRatisResponse.encode(result, method.getReturnType());
     } catch (NoSuchMethodException | SecurityException ex) {
       throw new InvalidProtocolBufferException(ex.getMessage());
     } catch (InvocationTargetException e) {
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmCodecFactory.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmCodecFactory.java
index d4f92eb53f3..df6ec265616 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmCodecFactory.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmCodecFactory.java
@@ -21,16 +21,15 @@
 import com.google.protobuf.ProtocolMessageEnum;
 import java.math.BigInteger;
 import java.security.cert.X509Certificate;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.IntFunction;
-import org.apache.commons.lang3.ClassUtils;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ContainerID;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ContainerInfoProto;
 import 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.DeletedBlocksTransactionSummary;
@@ -49,9 +48,12 @@
  */
 public final class ScmCodecFactory {
 
-  private static Map<Class<?>, ScmCodec<?>> codecs = new HashMap<>();
+  private final Map<Class<?>, ScmCodec<?>> codecs = new HashMap<>();
+  private final Map<String, Class<?>> classes = new ConcurrentHashMap<>();
+  private final ClassResolver resolver;
+  private static final ScmCodecFactory INSTANCE = new ScmCodecFactory();
 
-  static {
+  private ScmCodecFactory() {
     putProto(ContainerID.getDefaultInstance());
     putProto(PipelineID.getDefaultInstance());
     putProto(Pipeline.getDefaultInstance());
@@ -74,35 +76,55 @@ public final class ScmCodecFactory {
     putEnum(NodeType.class, NodeType::forNumber);
 
     // Must be the last one
-    final ClassResolver resolver = new ClassResolver(codecs.keySet());
+    resolver = new ClassResolver(codecs.keySet());
     codecs.put(List.class, new ScmListCodec(resolver));
   }
 
-  static <T extends Message> void putProto(T proto) {
+  private <T extends Message> void putProto(T proto) {
     final Class<? extends Message> clazz = proto.getClass();
     codecs.put(clazz, new 
ScmNonShadedGeneratedMessageCodec<>(clazz.getSimpleName(), 
proto.getParserForType()));
   }
 
-  static <T extends Enum<T> & ProtocolMessageEnum> void putEnum(
+  private <T extends Enum<T> & ProtocolMessageEnum> void putEnum(
       Class<T> enumClass, IntFunction<T> forNumber) {
     codecs.put(enumClass, new ScmEnumCodec<>(enumClass, forNumber));
   }
 
-  private ScmCodecFactory() { }
-
-  public static ScmCodec getCodec(Class<?> type)
-      throws InvalidProtocolBufferException {
-    final List<Class<?>> classes = new ArrayList<>();
-    classes.add(type);
-    classes.addAll(ClassUtils.getAllSuperclasses(type));
-    classes.addAll(ClassUtils.getAllInterfaces(type));
-    for (Class<?> clazz : classes) {
-      if (codecs.containsKey(clazz)) {
-        return codecs.get(clazz);
-      }
+  public static ScmCodecFactory getInstance() {
+    return INSTANCE;
+  }
+
+  public Class<?> resolve(String className) throws 
InvalidProtocolBufferException {
+    return resolver.get(className);
+  }
+
+  public Class<?> resolve(Class<?> clazz) throws 
InvalidProtocolBufferException {
+    return resolver.get(clazz);
+  }
+
+  public ScmCodec getCodec(Class<?> resolved) throws 
InvalidProtocolBufferException {
+    final ScmCodec<?> codec = codecs.get(resolved);
+    if (codec != null) {
+      return codec;
     }
-    throw new InvalidProtocolBufferException(
-        "Codec for " + type + " not found!");
+
+    throw new InvalidProtocolBufferException("Codec not found for " + 
resolved);
+  }
+
+  public Class<?> getClass(String className) throws 
InvalidProtocolBufferException {
+    final Class<?> found = classes.get(className);
+    if (found != null) {
+      return found;
+    }
+
+    final Class<?> clazz;
+    try {
+      clazz = Class.forName(className);
+    } catch (ClassNotFoundException e) {
+      throw new InvalidProtocolBufferException("Class not found for " + 
className, e);
+    }
+    classes.put(className, clazz);
+    return clazz;
   }
 
   /** Resolve the codec class from a given class. */
@@ -120,31 +142,37 @@ static class ClassResolver {
     }
 
     Class<?> get(String className) throws InvalidProtocolBufferException {
-      final Class<?> c = provided.get(className);
-      if (c != null) {
-        return c;
-      }
-      throw new InvalidProtocolBufferException("Class not found for " + 
className);
+      return get(null, className);
     }
 
     Class<?> get(Class<?> clazz) throws InvalidProtocolBufferException {
-      final String className = clazz.getName();
+      return get(clazz, clazz.getName());
+    }
+
+    private Class<?> get(Class<?> clazz, String className) throws 
InvalidProtocolBufferException {
+      Objects.requireNonNull(className, "className == null");
       final Class<?> c = provided.get(className);
       if (c != null) {
         return c;
       }
+
       final Class<?> found = resolved.get(className);
       if (found != null) {
         return found;
       }
 
+      if (clazz == null) {
+        clazz = getInstance().getClass(className);
+      }
+
       for (Class<?> base : provided.values()) {
         if (base.isAssignableFrom(clazz)) {
           resolved.put(className, base);
           return base;
         }
       }
-      throw new InvalidProtocolBufferException("Failed to resolve " + clazz);
+
+      throw new InvalidProtocolBufferException("Failed to resolve " + 
className);
     }
   }
 }
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmListCodec.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmListCodec.java
index cb426356b22..36c0531b812 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmListCodec.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/io/ScmListCodec.java
@@ -50,7 +50,8 @@ public ByteString serialize(Object object) throws 
InvalidProtocolBufferException
     }
 
     final Class<?> resolved = resolver.get(elements.get(0).getClass());
-    final ScmCodec<Object> elementCodec = ScmCodecFactory.getCodec(resolved);
+    final ScmCodec<Object> elementCodec = 
ScmCodecFactory.getInstance().getCodec(resolved);
+
     final ListArgument.Builder builder = ListArgument.newBuilder()
         .setType(resolved.getName());
     for (Object e : elements) {
@@ -67,8 +68,10 @@ public Object deserialize(ByteString value) throws 
InvalidProtocolBufferExceptio
       throw new InvalidProtocolBufferException(
           "Missing ListArgument.type: " + argument);
     }
+
     final Class<?> elementClass = resolver.get(argument.getType());
-    final ScmCodec<?> elementCodec = ScmCodecFactory.getCodec(elementClass);
+
+    final ScmCodec<?> elementCodec = 
ScmCodecFactory.getInstance().getCodec(elementClass);
     final List<Object> list = new ArrayList<>();
     for (ByteString element : argument.getValueList()) {
       list.add(elementCodec.deserialize(element));
diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisResponse.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisResponse.java
index b65aa3ac885..fd3df78b08a 100644
--- 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisResponse.java
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSCMRatisResponse.java
@@ -67,7 +67,7 @@ public void testEncodeAndDecodeSuccess() throws Exception {
         .build();
     SCMRatisResponse response = SCMRatisResponse.decode(reply);
     assertTrue(response.isSuccess());
-    assertEquals(Message.EMPTY, SCMRatisResponse.encode(response.getResult()));
+    assertEquals(Message.EMPTY, SCMRatisResponse.encode(response.getResult(), 
Object.class));
   }
 
   @Test
@@ -94,7 +94,7 @@ public void testEncodeFailureWithNonProto() {
     Message message = Message.valueOf("test");
     // Should fail with exception.
     assertThrows(InvalidProtocolBufferException.class,
-        () -> SCMRatisResponse.encode(message));
+        () -> SCMRatisResponse.encode(message, message.getClass()));
   }
 
   @Test


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

Reply via email to