You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ad...@apache.org on 2026/04/07 10:33:05 UTC
(ozone) branch master updated: HDDS-14973. Update ScmInvokerCodeGenerator to generate getProxy() (#10049)
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 9e775e6c8f6 HDDS-14973. Update ScmInvokerCodeGenerator to generate getProxy() (#10049)
9e775e6c8f6 is described below
commit 9e775e6c8f6217562dda239b1e38a25e44265808
Author: Tsz-Wo Nicholas Sze <sz...@apache.org>
AuthorDate: Tue Apr 7 03:32:59 2026 -0700
HDDS-14973. Update ScmInvokerCodeGenerator to generate getProxy() (#10049)
---
.../scm/block/DeletedBlockLogStateManager.java | 4 +-
.../hdds/scm/ha/ScmInvokerCodeGenerator.java | 240 ----------
.../DeletedBlockLogStateManagerInvoker.java | 133 +++--
.../hadoop/hdds/scm/ha/invoker/ScmInvoker.java | 23 +-
.../scm/ha/invoker/ScmInvokerCodeGenerator.java | 533 +++++++++++++++++++++
5 files changed, 606 insertions(+), 327 deletions(-)
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/block/DeletedBlockLogStateManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/block/DeletedBlockLogStateManager.java
index 6a8732cf8c8..41616527661 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/block/DeletedBlockLogStateManager.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/block/DeletedBlockLogStateManager.java
@@ -24,7 +24,7 @@
import org.apache.hadoop.hdds.protocol.proto.SCMRatisProtocol.RequestType;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.DeletedBlocksTransaction;
import org.apache.hadoop.hdds.scm.ha.SCMHandler;
-import org.apache.hadoop.hdds.scm.ha.ScmInvokerCodeGenerator;
+import org.apache.hadoop.hdds.scm.ha.invoker.ScmInvokerCodeGenerator;
import org.apache.hadoop.hdds.scm.metadata.Replicate;
import org.apache.hadoop.hdds.utils.db.Table;
@@ -77,6 +77,6 @@ void reinitialize(Table<Long, DeletedBlocksTransaction> deletedBlocksTXTable,
Table<String, ByteString> statefulConfigTable);
static void main(String[] args) {
- new ScmInvokerCodeGenerator(DeletedBlockLogStateManager.class, RequestType.BLOCK).generateClass();
+ ScmInvokerCodeGenerator.generate(DeletedBlockLogStateManager.class, true);
}
}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ScmInvokerCodeGenerator.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ScmInvokerCodeGenerator.java
deleted file mode 100644
index cbe18118644..00000000000
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/ScmInvokerCodeGenerator.java
+++ /dev/null
@@ -1,240 +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.io.PrintStream;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Parameter;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
-import org.apache.hadoop.hdds.protocol.proto.SCMRatisProtocol.RequestType;
-import org.apache.ratis.util.UncheckedAutoCloseable;
-
-/**
- * A tool to (manually) generate code for copy-paste.
- */
-public class ScmInvokerCodeGenerator {
- static final DeclaredMethod INVOKE_LOCAL = new DeclaredMethod("invokeLocal",
- new Class[]{String.class, Object[].class},
- new String[]{"methodName", "params"},
- Object.class,
- new Class<?>[]{Exception.class});
-
- private final Class<?> api;
- private final String apiName;
- private final String requestTypeName;
- private final String invokerClassName;
-
- private final PrintStream out = System.out;
- private String indentation = "";
-
- public ScmInvokerCodeGenerator(Class<?> api, RequestType type) {
- this.api = api;
- this.apiName = api.getSimpleName();
- this.requestTypeName = type.name();
- this.invokerClassName = apiName + "Invoker";
- }
-
- void printf(String format, Object... args) {
- out.printf(indentation + format, args);
- }
-
- void println(String format, Object... args) {
- printf(format, args);
- out.println();
- }
-
- UncheckedAutoCloseable printScope() {
- return printScope(true, true);
- }
-
- UncheckedAutoCloseable printScope(boolean codeBlock, boolean intend) {
- out.println(codeBlock ? " {" : "");
- if (intend) {
- indentation += " ";
- }
- return () -> {
- if (intend) {
- indentation = indentation.substring(2);
- }
- if (codeBlock) {
- println("}");
- }
- };
- }
-
- static String classesToString(Class<?>[] classes) {
- return arrayToString(classes, Class::getSimpleName);
- }
-
- static <T> String arrayToString(T[] objects, Function<T, String> toString) {
- return Arrays.stream(objects)
- .map(toString)
- .reduce(null, (a, b) -> a == null ? b : a + ", " + b);
- }
-
- void printHeaderMethods() {
- out.println();
- printf("public %s(%s impl)", invokerClassName, apiName);
- try (UncheckedAutoCloseable ignore = printScope()) {
- println("this.impl = impl;");
- }
-
- out.println();
- println("@Override");
- printf("public RequestType getType()");
- try (UncheckedAutoCloseable ignore = printScope()) {
- println("return RequestType.%s;", requestTypeName);
- }
-
- out.println();
- println("@Override");
- printf("public Class<%s> getApi()", apiName);
- try (UncheckedAutoCloseable ignore = printScope()) {
- println("return %s.class;", apiName);
- }
-
- out.println();
- println("@Override");
- printf("public %s getImpl()", apiName);
- try (UncheckedAutoCloseable ignore = printScope()) {
- println("return impl;");
- }
- }
-
- void printMethodSignature(DeclaredMethod method) {
- printf("public %s %s(%s) throws %s",
- method.getReturnType().getSimpleName(),
- method.getName(),
- method.getParameterString(),
- classesToString(method.getExceptionTypes()));
- }
-
- void printCase(Method apiMethod, String actualParameter, AtomicInteger argCount) {
- printf("case \"%s\":", apiMethod.getName());
- try (UncheckedAutoCloseable ignore = printScope(false, true)) {
- final Parameter[] apiParameters = apiMethod.getParameters();
- final StringBuilder b = new StringBuilder();
- for (int i = 0; i < apiParameters.length; i++) {
- final Parameter p = apiParameters[i];
- final String classname = p.getType().getSimpleName();
- final String arg = "arg" + argCount.getAndIncrement();
- b.append(arg).append(", ");
- println("final %s %s = %s.length > %d ? (%s) %s[%d] : null;", classname, arg,
- actualParameter, i, classname, actualParameter, i);
- }
- if (b.length() > 0) {
- b.setLength(b.length() - 2);
- }
- if (apiMethod.getReturnType() == void.class) {
- println("impl.%s(%s);", apiMethod.getName(), b);
- println("return null;");
- } else {
- println("return impl.%s(%s);", apiMethod.getName(), b);
- }
- }
- out.println();
- }
-
- void printSwitch(DeclaredMethod method) {
- final String switchName = method.getParameterName(0);
- printf("switch (%s)", switchName);
- try (UncheckedAutoCloseable ignored = printScope(true, false)) {
- final Method[] apiMethods = api.getMethods();
- Arrays.sort(apiMethods, Comparator.comparing(Method::getName));
- final AtomicInteger argCount = new AtomicInteger(0);
- for (Method apiMethod : apiMethods) {
- if (!apiMethod.isDefault() && !Modifier.isStatic(apiMethod.getModifiers())) {
- printCase(apiMethod, method.getParameterName(1), argCount);
- }
- }
-
- printf("default:");
- try (UncheckedAutoCloseable ignore = printScope(false, true)) {
- println("throw new IllegalArgumentException(\"Method not found: \" + %s + \" in %s\");",
- switchName, apiName);
- }
- }
- }
-
- void printInvokeMethod(DeclaredMethod method) {
- out.println();
- println("@SuppressWarnings(\"unchecked\")");
- println("@Override");
- printMethodSignature(method);
- try (UncheckedAutoCloseable ignored = printScope()) {
- printSwitch(method);
- }
- }
-
- public void generateClass() {
- println("/** Code generated for %s. Do not modify. */", apiName);
- printf("public class %s implements ScmInvoker<%s>", invokerClassName, apiName);
- try (UncheckedAutoCloseable ignored = printScope()) {
- println("private final %s impl;", apiName);
- printHeaderMethods();
- printInvokeMethod(INVOKE_LOCAL);
- }
- }
-
- static class DeclaredMethod {
- private final String name;
- private final Class<?>[] parameterTypes;
- private final String[] parameterNames;
- private final Class<?> returnType;
- private final Class<?>[] exceptionTypes;
-
- DeclaredMethod(String name, Class<?>[] parameterTypes, String[] parameterNames,
- Class<?> returnType, Class<?>[] exceptionTypes) {
- this.name = name;
- this.parameterTypes = parameterTypes;
- this.parameterNames = parameterNames;
- this.returnType = returnType;
- this.exceptionTypes = exceptionTypes;
- }
-
- String getName() {
- return name;
- }
-
- String getParameterName(int i) {
- return parameterNames[i];
- }
-
- String getParameterString() {
- final StringBuilder b = new StringBuilder();
- for (int i = 0; i < parameterNames.length; i++) {
- b.append(parameterTypes[i].getSimpleName()).append(' ')
- .append(parameterNames[i]).append(", ");
- }
- b.setLength(b.length() - 2);
- return b.toString();
- }
-
- Class<?> getReturnType() {
- return returnType;
- }
-
- Class<?>[] getExceptionTypes() {
- return exceptionTypes;
- }
- }
-}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/DeletedBlockLogStateManagerInvoker.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/DeletedBlockLogStateManagerInvoker.java
index 3a5c450640b..862481746d8 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/DeletedBlockLogStateManagerInvoker.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/DeletedBlockLogStateManagerInvoker.java
@@ -17,39 +17,32 @@
package org.apache.hadoop.hdds.scm.ha.invoker;
-import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DeletedBlocksTransactionSummary;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.DeletedBlocksTransaction;
import org.apache.hadoop.hdds.scm.block.DeletedBlockLogStateManager;
import org.apache.hadoop.hdds.scm.ha.SCMRatisServer;
import org.apache.hadoop.hdds.utils.db.Table;
-/**
- * Invoker for DeletedBlockLogStateManager local (non-@Replicate) methods.
- */
+/** Code generated for {@link DeletedBlockLogStateManager}. Do not modify. */
public class DeletedBlockLogStateManagerInvoker extends ScmInvoker<DeletedBlockLogStateManager> {
- private final DeletedBlockLogStateManager impl;
-
enum ReplicateMethod implements NameAndParameterTypes {
- addTransactionsToDB(new Class<?>[] {ArrayList.class, DeletedBlocksTransactionSummary.class}),
- removeTransactionsFromDB(new Class<?>[] {ArrayList.class, DeletedBlocksTransactionSummary.class}),;
+ addTransactionsToDB(new Class<?>[][] {
+ null,
+ new Class<?>[] {ArrayList.class},
+ new Class<?>[] {ArrayList.class, DeletedBlocksTransactionSummary.class}
+ }),
+ removeTransactionsFromDB(new Class<?>[][] {
+ null,
+ new Class<?>[] {ArrayList.class},
+ new Class<?>[] {ArrayList.class, DeletedBlocksTransactionSummary.class}
+ });
private final Class<?>[][] parameterTypes;
- ReplicateMethod(Class<?>[] parameterTypes) {
- final Class<?>[][] types = new Class<?>[parameterTypes.length + 1][];
- for (int i = 0; i <= parameterTypes.length; ++i) {
- types[i] = Arrays.copyOf(parameterTypes, i);
- }
- this.parameterTypes = types;
- }
-
- @Override
- public String getName() {
- return name();
+ ReplicateMethod(Class<?>[][] parameterTypes) {
+ this.parameterTypes = parameterTypes;
}
@Override
@@ -58,9 +51,8 @@ public Class<?>[] getParameterTypes(int numArgs) {
}
}
- public DeletedBlockLogStateManagerInvoker(DeletedBlockLogStateManager impl, SCMRatisServer scmRatisServer) {
- super(scmRatisServer);
- this.impl = impl;
+ public DeletedBlockLogStateManagerInvoker(DeletedBlockLogStateManager impl, SCMRatisServer ratis) {
+ super(impl, DeletedBlockLogStateManagerInvoker::newProxy, ratis);
}
@Override
@@ -68,98 +60,81 @@ public Class<DeletedBlockLogStateManager> getApi() {
return DeletedBlockLogStateManager.class;
}
- @Override
- public DeletedBlockLogStateManager getImpl() {
- return impl;
- }
-
- @Override
- public DeletedBlockLogStateManager getProxy() {
+ static DeletedBlockLogStateManager newProxy(ScmInvoker<DeletedBlockLogStateManager> invoker) {
return new DeletedBlockLogStateManager() {
+
@Override
- public void addTransactionsToDB(ArrayList<DeletedBlocksTransaction> txs)
- throws IOException {
- final Object[] args = {txs};
- invokeRatisServer(ReplicateMethod.addTransactionsToDB, args);
+ public void addTransactionsToDB(ArrayList arg0) throws IOException {
+ final Object[] args = {arg0};
+ invoker.invokeReplicateDirect(ReplicateMethod.addTransactionsToDB, args);
}
@Override
- public void addTransactionsToDB(ArrayList<DeletedBlocksTransaction> txs,
- DeletedBlocksTransactionSummary summary) throws IOException {
- final Object[] args = {txs, summary};
- invokeRatisServer(ReplicateMethod.addTransactionsToDB, args);
+ public void addTransactionsToDB(ArrayList arg0, DeletedBlocksTransactionSummary arg1) throws IOException {
+ final Object[] args = {arg0, arg1};
+ invoker.invokeReplicateDirect(ReplicateMethod.addTransactionsToDB, args);
}
@Override
- public void removeTransactionsFromDB(ArrayList<Long> txIDs)
- throws IOException {
- final Object[] args = {txIDs};
- invokeRatisServer(ReplicateMethod.removeTransactionsFromDB, args);
+ public Table.KeyValueIterator<Long, DeletedBlocksTransaction> getReadOnlyIterator() throws IOException {
+ return invoker.getImpl().getReadOnlyIterator();
}
@Override
- public void removeTransactionsFromDB(ArrayList<Long> txIDs,
- DeletedBlocksTransactionSummary summary) throws IOException {
- final Object[] args = {txIDs, summary};
- invokeRatisServer(ReplicateMethod.removeTransactionsFromDB, args);
+ public void onFlush() {
+ invoker.getImpl().onFlush();
}
@Override
- public Table.KeyValueIterator<Long, DeletedBlocksTransaction> getReadOnlyIterator() throws IOException {
- return impl.getReadOnlyIterator();
+ public void reinitialize(Table arg0, Table arg1) {
+ invoker.getImpl().reinitialize(arg0, arg1);
}
@Override
- public void onFlush() {
- impl.onFlush();
+ public void removeTransactionsFromDB(ArrayList arg0) throws IOException {
+ final Object[] args = {arg0};
+ invoker.invokeReplicateDirect(ReplicateMethod.removeTransactionsFromDB, args);
}
@Override
- public void reinitialize(
- Table<Long, DeletedBlocksTransaction> deletedBlocksTXTable,
- Table<String, ByteString> statefulConfigTable) {
- impl.reinitialize(deletedBlocksTXTable, statefulConfigTable);
+ public void removeTransactionsFromDB(ArrayList arg0, DeletedBlocksTransactionSummary arg1) throws IOException {
+ final Object[] args = {arg0, arg1};
+ invoker.invokeReplicateDirect(ReplicateMethod.removeTransactionsFromDB, args);
}
};
}
- // Code generated for DeletedBlockLogStateManager. Do not modify.
@SuppressWarnings("unchecked")
@Override
- public Object invokeLocal(String methodName, Object[] params) throws Exception {
+ public Object invokeLocal(String methodName, Object[] p) throws Exception {
switch (methodName) {
- case "onFlush":
- impl.onFlush();
- return null;
-
case "addTransactionsToDB":
- final ArrayList arg0 =
- params.length > 0 ? (ArrayList) params[0] : null;
- final DeletedBlocksTransactionSummary arg1 =
- params.length > 1 ? (DeletedBlocksTransactionSummary) params[1] : null;
- impl.addTransactionsToDB(arg0, arg1);
- return null;
-
- case "removeTransactionsFromDB":
- final ArrayList arg2 =
- params.length > 0 ? (ArrayList) params[0] : null;
- final DeletedBlocksTransactionSummary arg3 =
- params.length > 1 ? (DeletedBlocksTransactionSummary) params[1] : null;
- impl.removeTransactionsFromDB(arg2, arg3);
+ final ArrayList arg0 = p.length > 0 ? (ArrayList) p[0] : null;
+ final DeletedBlocksTransactionSummary arg1 = p.length > 1 ? (DeletedBlocksTransactionSummary) p[1] : null;
+ getImpl().addTransactionsToDB(arg0, arg1);
return null;
case "getReadOnlyIterator":
- return impl.getReadOnlyIterator();
+ return getImpl().getReadOnlyIterator();
+
+ case "onFlush":
+ getImpl().onFlush();
+ return null;
case "reinitialize":
- final Table arg4 = params.length > 0 ? (Table) params[0] : null;
- final Table arg5 = params.length > 1 ? (Table) params[1] : null;
- impl.reinitialize(arg4, arg5);
+ final Table arg2 = p.length > 0 ? (Table) p[0] : null;
+ final Table arg3 = p.length > 1 ? (Table) p[1] : null;
+ getImpl().reinitialize(arg2, arg3);
+ return null;
+
+ case "removeTransactionsFromDB":
+ final ArrayList arg4 = p.length > 0 ? (ArrayList) p[0] : null;
+ final DeletedBlocksTransactionSummary arg5 = p.length > 1 ? (DeletedBlocksTransactionSummary) p[1] : null;
+ getImpl().removeTransactionsFromDB(arg4, arg5);
return null;
default:
- throw new IllegalArgumentException("Method not found: " + methodName);
+ throw new IllegalArgumentException("Method not found: " + methodName + " in DeletedBlockLogStateManager");
}
}
-
}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvoker.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvoker.java
index 8aa7e71a496..6c8dad6d1d7 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvoker.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvoker.java
@@ -19,6 +19,7 @@
import static org.apache.hadoop.hdds.scm.ha.SCMHAInvocationHandler.translateException;
+import java.util.function.Function;
import org.apache.hadoop.hdds.protocol.proto.SCMRatisProtocol.RequestType;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.hdds.scm.ha.SCMHandler;
@@ -30,9 +31,13 @@
* Invokes methods without using reflection.
*/
public abstract class ScmInvoker<T extends SCMHandler> {
+ private final T impl;
+ private final T proxy;
private final SCMRatisServer ratisHandler;
- ScmInvoker(SCMRatisServer ratisHandler) {
+ ScmInvoker(T impl, Function<ScmInvoker<T>, T> proxy, SCMRatisServer ratisHandler) {
+ this.impl = impl;
+ this.proxy = proxy.apply(this);
this.ratisHandler = ratisHandler;
}
@@ -42,16 +47,22 @@ public final RequestType getType() {
public abstract Class<T> getApi();
- public abstract T getImpl();
+ public final T getImpl() {
+ return impl;
+ }
- public abstract T getProxy();
+ public final T getProxy() {
+ return proxy;
+ }
+ /** For non-@Replicate methods. */
abstract Object invokeLocal(String methodName, Object[] args) throws Exception;
- Object invokeRatisServer(NameAndParameterTypes method, Object[] args) throws SCMException {
+ /** For @Replicate DIRECT methods. */
+ final Object invokeReplicateDirect(NameAndParameterTypes method, Object[] args) throws SCMException {
try {
final SCMRatisRequest request = SCMRatisRequest.of(
- getType(), method.getName(), method.getParameterTypes(args.length), args);
+ getType(), method.name(), method.getParameterTypes(args.length), args);
final SCMRatisResponse response = ratisHandler.submitRequest(request);
if (response.isSuccess()) {
return response.getResult();
@@ -63,7 +74,7 @@ Object invokeRatisServer(NameAndParameterTypes method, Object[] args) throws SCM
}
interface NameAndParameterTypes {
- String getName();
+ String name();
Class<?>[] getParameterTypes(int numArgs);
}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvokerCodeGenerator.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvokerCodeGenerator.java
new file mode 100644
index 00000000000..e9898b9532d
--- /dev/null
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/ScmInvokerCodeGenerator.java
@@ -0,0 +1,533 @@
+/*
+ * 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.invoker;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.apache.hadoop.hdds.scm.metadata.Replicate;
+import org.apache.ratis.util.Preconditions;
+import org.apache.ratis.util.UncheckedAutoCloseable;
+
+/**
+ * Generate code for {@link ScmInvoker} implementations.
+ * Step 1. Create the target java file in {@link #DIR}. It will be used as an input for license header and imports.
+ * Step 2. Add main method to the API interface.
+ * Step 3. Manually fix imports.
+ * <p>
+ * Below is an example for generating the API interface FinalizationStateManager:
+ * Step 1. Copy FinalizationStateManager.java to DIR/FinalizationStateManagerInvoker.java
+ * Step 2. //FinalizationStateManager
+ * static void main(String[] args) {
+ * ScmInvokerCodeGenerator.generate(FinalizationStateManager.class, true);
+ * }
+ * Step 3. Manually fix imports.
+ */
+public final class ScmInvokerCodeGenerator {
+ static final String DIR = "hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/invoker/";
+ static final int LINE_LENGTH = 120;
+
+ static final DeclaredMethod INVOKE_LOCAL = new DeclaredMethod("invokeLocal",
+ new Class[]{String.class, Object[].class},
+ new String[]{"methodName", "p"},
+ Object.class,
+ new Class<?>[]{Exception.class});
+
+ private final Class<?> api;
+ private final String apiName;
+ private final String invokerClassName;
+
+ private final StringWriter lineBuffer = new StringWriter();
+ private final StringWriter out = new StringWriter();
+ private String indentation = "";
+
+ private ScmInvokerCodeGenerator(Class<?> api) {
+ this.api = api;
+ this.apiName = api.getSimpleName();
+ this.invokerClassName = apiName + "Invoker";
+
+ }
+
+ void printf(String format, Object... args) {
+ printf(true, format, args);
+ }
+
+ void printf(boolean indent, String format, Object... args) {
+ if (indent) {
+ lineBuffer.append(indentation);
+ }
+ lineBuffer.append(String.format(format, args));
+ }
+
+ void println() {
+ println(false, "");
+ }
+
+ void println(String format, Object... args) {
+ println(true, format, args);
+ }
+
+ void println(boolean indent, String format, Object... args) {
+ printf(indent, format, args);
+
+ String s = lineBuffer.toString();
+ lineBuffer.getBuffer().setLength(0);
+
+ if (s.length() <= LINE_LENGTH) {
+ out.append(s).append(System.lineSeparator());
+ return;
+ }
+
+ // break long lines
+ int nonSpace = 0;
+ while (s.charAt(nonSpace) == ' ') {
+ nonSpace++;
+ }
+
+ final int i = s.lastIndexOf(' ', LINE_LENGTH);
+ out.append(s.substring(0, i))
+ .append(System.lineSeparator());
+ out.append(s.substring(0, nonSpace)) // original indentation
+ .append(" ")
+ .append(s.substring(i + 1))
+ .append(System.lineSeparator());
+ }
+
+ UncheckedAutoCloseable printScope() {
+ return printScope(true, 1);
+ }
+
+ UncheckedAutoCloseable printScope(boolean codeBlock, int intendLevel) {
+ println(false, codeBlock ? " {" : "");
+ for (int i = 0; i < intendLevel; i++) {
+ indentation += " ";
+ }
+ return () -> {
+ if (intendLevel > 0) {
+ indentation = indentation.substring(2 * intendLevel);
+ }
+ if (codeBlock) {
+ println("}");
+ }
+ };
+ }
+
+ static String getClassname(Class<?> clazz) {
+ return getClassnameBuilder(clazz).toString();
+ }
+
+ static StringBuilder getClassnameBuilder(Class<?> clazz) {
+ final StringBuilder b = new StringBuilder(clazz.getSimpleName());
+ for (Class<?> c = clazz.getEnclosingClass(); c != null; c = c.getEnclosingClass()) {
+ final String name = c.getSimpleName();
+ if (name.endsWith("Protos")) {
+ break;
+ }
+ b.insert(0, name + ".");
+ }
+ return b;
+ }
+
+ static String getReturnTypeString(Method method) {
+ final StringBuilder b = getClassnameBuilder(method.getReturnType());
+ final String generic = method.getGenericReturnType().getTypeName();
+ int i = generic.indexOf('<');
+ if (i < 0) {
+ return b.toString();
+ }
+ b.append('<');
+ for (boolean more = true; more;) {
+ final int j = generic.indexOf(", ", i);
+ more = j >= 0;
+ try {
+ final Class<?> clazz = Class.forName(generic.substring(i + 1, more ? j : generic.length() - 1));
+ b.append(getClassname(clazz)).append(", ");
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Failed to getReturnTypeString for " + generic, e);
+ }
+ i = j + 1;
+ }
+ b.setLength(b.length() - 2);
+ return b.append('>').toString();
+ }
+
+ static String classesToString(Class<?>[] classes, String suffix) {
+ return arrayToString(classes, c -> getClassnameBuilder(c).append(suffix).toString());
+ }
+
+ static <T> String arrayToString(T[] objects, Function<T, String> toString) {
+ return Arrays.stream(objects)
+ .map(toString)
+ .reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
+ }
+
+ static String getThrowsString(Class<?>[] exceptions) {
+ if (exceptions.length == 0) {
+ return "";
+ }
+ return " throws " + classesToString(exceptions, "");
+ }
+
+ static String getParameterString(Class<?>[] types, String[] names) {
+ final StringBuilder b = new StringBuilder();
+ final int n = names != null ? names.length : types.length;
+ for (int i = 0; i < n; i++) {
+ b.append(getClassname(types[i])).append(' ')
+ .append(names != null ? names[i] : "arg" + i).append(", ");
+ }
+ if (b.length() > 0) {
+ b.setLength(b.length() - 2);
+ }
+ return b.toString();
+ }
+
+ static Predicate<Method> getFilter(Boolean isDefault, Boolean isDeprecated) {
+ return m -> {
+ if (Modifier.isStatic(m.getModifiers())) {
+ return false;
+ }
+ if (isDefault != null && isDefault != m.isDefault()) {
+ return false;
+ }
+ if (isDeprecated != null && isDeprecated == (m.getAnnotation(Deprecated.class) == null)) {
+ return false;
+ }
+ return true;
+ };
+ }
+
+ List<Method> getMethods(Boolean isDefault, Boolean isDeprecated) {
+ return getMethods(getFilter(isDefault, isDeprecated));
+ }
+
+ List<Method> getMethods(Predicate<Method> filter) {
+ return Arrays.stream(api.getMethods())
+ .filter(filter)
+ .sorted(Comparator.comparing(Method::getName).thenComparing(Method::getParameterCount))
+ .collect(Collectors.toList());
+ }
+
+ void printEnum() {
+ printf("enum ReplicateMethod implements NameAndParameterTypes");
+ try (UncheckedAutoCloseable ignore = printScope()) {
+ final List<Method> apiMethods = getMethods(null, null);
+ boolean first = true;
+ for (Method m : apiMethods) {
+ if (m.isDefault() || m.getAnnotation(Replicate.class) == null) {
+ continue;
+ }
+ if (first) {
+ first = false;
+ } else {
+ println(false, ",");
+ }
+
+ final List<Method> overrides = apiMethods.stream()
+ .filter(method -> method.getName().equals(m.getName()))
+ .sorted(Comparator.comparing(Method::getParameterCount))
+ .collect(Collectors.toList());
+ printEnumConstant(overrides);
+ }
+ println(false, ";");
+
+ printEnumBody();
+ }
+ }
+
+ private void printEnumBody() {
+ println();
+ println("private final Class<?>[][] parameterTypes;");
+
+ println();
+ printf("ReplicateMethod(Class<?>[][] parameterTypes)");
+ try (UncheckedAutoCloseable ignore = printScope()) {
+ println("this.parameterTypes = parameterTypes;");
+ }
+
+ println();
+ println("@Override");
+ printf("public Class<?>[] getParameterTypes(int numArgs)");
+ try (UncheckedAutoCloseable ignore = printScope()) {
+ println("return parameterTypes[numArgs];");
+ }
+ }
+
+ private void printEnumConstant(List<Method> overrides) {
+ final Class<?>[][] parameterTypes = getParameterTypes(overrides);
+ printf("%s(new Class<?>[][] {", overrides.get(0).getName());
+ try (UncheckedAutoCloseable ignore = printScope(false, 2)) {
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (i > 0) {
+ println(false, ",");
+ }
+
+ final Class<?>[] classes = parameterTypes[i];
+ if (classes == null) {
+ printf("null");
+ } else {
+ printf("new Class<?>[] {%s}", classesToString(classes, ".class"));
+ }
+ }
+ }
+ println();
+ printf("})");
+ }
+
+ static Class<?>[][] getParameterTypes(List<Method> overrides) {
+ final Method last = overrides.get(overrides.size() - 1);
+ final Class<?>[][] types = new Class<?>[last.getParameterCount() + 1][];
+ for (Method method : overrides) {
+ Preconditions.assertEquals(last.getName(), method.getName(), "methodName");
+ final int i = method.getParameterCount();
+ if (types[i] != null) {
+ throw new IllegalArgumentException("Duplicate method parameters found: " + overrides);
+ }
+ types[i] = method.getParameterTypes();
+ }
+ return types;
+ }
+
+ void printHeaderMethods() {
+ println();
+ printf("public %s(%s impl, SCMRatisServer ratis)", invokerClassName, apiName);
+ try (UncheckedAutoCloseable ignore = printScope()) {
+ println("super(impl, %s::newProxy, ratis);", invokerClassName);
+ }
+
+ println();
+ println("@Override");
+ printf("public Class<%s> getApi()", apiName);
+ try (UncheckedAutoCloseable ignore = printScope()) {
+ println("return %s.class;", apiName);
+ }
+ }
+
+ void printCase(Method apiMethod, String actualParameter, AtomicInteger argCount) {
+ printf("case \"%s\":", apiMethod.getName());
+ try (UncheckedAutoCloseable ignore = printScope(false, 1)) {
+ final Parameter[] apiParameters = apiMethod.getParameters();
+ final StringBuilder b = new StringBuilder();
+ for (int i = 0; i < apiParameters.length; i++) {
+ final Parameter p = apiParameters[i];
+ final String classname = getClassname(p.getType());
+ final String arg = "arg" + argCount.getAndIncrement();
+ b.append(arg).append(", ");
+ println("final %s %s = %s.length > %d ? (%s) %s[%d] : null;", classname, arg,
+ actualParameter, i, classname, actualParameter, i);
+ }
+ if (b.length() > 0) {
+ b.setLength(b.length() - 2);
+ }
+ if (apiMethod.getReturnType() == void.class) {
+ println("getImpl().%s(%s);", apiMethod.getName(), b);
+ println("return null;");
+ } else {
+ println("return getImpl().%s(%s);", apiMethod.getName(), b);
+ }
+ }
+ println();
+ }
+
+ void printSwitch(DeclaredMethod method) {
+ final String switchName = method.getParameterName(0);
+ printf("switch (%s)", switchName);
+ try (UncheckedAutoCloseable ignored = printScope(true, 0)) {
+ final AtomicInteger argCount = new AtomicInteger(0);
+ for (Method apiMethod : getMethods(false, null)) {
+ printCase(apiMethod, method.getParameterName(1), argCount);
+ }
+
+ printf("default:");
+ try (UncheckedAutoCloseable ignore = printScope(false, 1)) {
+ println("throw new IllegalArgumentException(\"Method not found: \" + %s + \" in %s\");",
+ switchName, apiName);
+ }
+ }
+ }
+
+ void printInvokeMethod(DeclaredMethod method) {
+ println();
+ println("@SuppressWarnings(\"unchecked\")");
+ println("@Override");
+ printf(method.getSignature());
+ try (UncheckedAutoCloseable ignored = printScope()) {
+ printSwitch(method);
+ }
+ }
+
+ void printProxyClassMethod(Method method) {
+ final Replicate r = method.getAnnotation(Replicate.class);
+ if (r == null && method.isDefault()) {
+ // Do not print non-Replicate default methods, just use default implementation.
+ return;
+ }
+
+ println();
+ println("@Override");
+ printf("public %s %s(%s)%s",
+ getReturnTypeString(method),
+ method.getName(),
+ getParameterString(method.getParameterTypes(), null),
+ getThrowsString(method.getExceptionTypes()));
+
+ try (UncheckedAutoCloseable ignored = printScope()) {
+ final String args = IntStream.range(0, method.getParameterCount())
+ .mapToObj(i -> "arg" + i)
+ .reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
+ final String returnString = method.getReturnType() == void.class ? "" : "return ";
+ if (r != null) {
+ final String type = r.invocationType() == Replicate.InvocationType.DIRECT ? "Direct" : "Client";
+ println("final Object[] args = {%s};", args);
+ println("%sinvoker.invokeReplicate%s(ReplicateMethod.%s, args);", returnString, type, method.getName());
+ } else {
+ println("%sinvoker.getImpl().%s(%s);", returnString, method.getName(), args);
+ }
+ }
+ }
+
+ void printProxyClass() {
+ printf("return new %s() {", apiName);
+ try (UncheckedAutoCloseable ignored = printScope(false, 1)) {
+ for (Method m : getMethods(null, false)) {
+ printProxyClassMethod(m);
+ }
+ }
+ println("};");
+ }
+
+ void printProxyMethod() {
+ println();
+ printf("static %s newProxy(ScmInvoker<%s> invoker)", apiName, apiName);
+ try (UncheckedAutoCloseable ignored = printScope()) {
+ printProxyClass();
+ }
+ }
+
+ public String generateClass() {
+ println("/** Code generated for {@link %s}. Do not modify. */", apiName);
+ printf("public class %s extends ScmInvoker<%s>", invokerClassName, apiName);
+ try (UncheckedAutoCloseable ignored = printScope()) {
+ printEnum();
+ printHeaderMethods();
+ printProxyMethod();
+ printInvokeMethod(INVOKE_LOCAL);
+ }
+ return out.toString();
+ }
+
+ File updateFile(String classString) throws IOException {
+ final File java = new File(DIR, invokerClassName + ".java");
+ if (!java.isFile()) {
+ throw new FileNotFoundException("Not found: " + java.getAbsolutePath());
+ }
+ final File tmp = new File(DIR, invokerClassName + "_tmp.java");
+ if (tmp.exists()) {
+ throw new IOException("Already exist: " + java.getAbsolutePath());
+ }
+ try (InputStream inStream = Files.newInputStream(java.toPath());
+ BufferedReader in = new BufferedReader(new InputStreamReader(new BufferedInputStream(inStream), UTF_8));
+ OutputStream outStream = Files.newOutputStream(tmp.toPath(), StandardOpenOption.CREATE_NEW);
+ PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, UTF_8), true)) {
+ String line;
+ for (; (line = in.readLine()) != null;) {
+ out.println(line);
+ if (line.startsWith("import")) {
+ break;
+ }
+ }
+ for (; (line = in.readLine()) != null && line.startsWith("import");) {
+ out.println(line);
+ }
+ out.println();
+ out.print(classString);
+ }
+
+ Files.move(tmp.toPath(), java.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ return java;
+ }
+
+ public static void generate(Class<?> api, boolean updateFile) {
+ final ScmInvokerCodeGenerator generator = new ScmInvokerCodeGenerator(api);
+ final String classString = generator.generateClass();
+ if (!updateFile) {
+ System.out.println(classString);
+ return;
+ }
+
+ final File file;
+ try {
+ file = generator.updateFile(classString);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to updateFile", e);
+ }
+ System.out.printf("Successfully update file: %s%n", file);
+ }
+
+ static class DeclaredMethod {
+ private final String name;
+ private final Class<?>[] parameterTypes;
+ private final String[] parameterNames;
+ private final Class<?> returnType;
+ private final Class<?>[] exceptionTypes;
+
+ DeclaredMethod(String name, Class<?>[] parameterTypes, String[] parameterNames,
+ Class<?> returnType, Class<?>[] exceptionTypes) {
+ this.name = name;
+ this.parameterTypes = parameterTypes;
+ this.parameterNames = parameterNames;
+ this.returnType = returnType;
+ this.exceptionTypes = exceptionTypes;
+ }
+
+ String getParameterName(int i) {
+ return parameterNames[i];
+ }
+
+ String getSignature() {
+ return String.format("public %s %s(%s)%s",
+ getClassname(returnType),
+ name,
+ getParameterString(parameterTypes, parameterNames),
+ getThrowsString(exceptionTypes));
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org