From 478821e278fc08746dc081e800e55bd7ad305f5e Mon Sep 17 00:00:00 2001 From: kaibocai <89094811+kaibocai@users.noreply.github.com> Date: Tue, 26 Sep 2023 15:20:25 -0500 Subject: [PATCH] clean up clean up more move resolver to dedicate package rename resolver to converter --- endtoendtests/pom.xml | 2 +- .../functions/worker/JavaWorkerClient.java | 2 +- .../worker/binding/BindingDataStore.java | 73 +++----- .../worker/binding/DataOperations.java | 11 +- .../worker/broker/FunctionDefinition.java | 2 +- .../worker/broker/JavaFunctionBroker.java | 4 +- .../worker/broker/JavaMethodExecutor.java | 5 +- .../worker/broker/JavaMethodInvokeInfo.java | 49 ------ .../worker/broker/ParamBindInfo.java | 8 +- .../ClassLoaderProvider.java | 2 +- .../DefaultClassLoaderProvider.java | 2 +- .../EnhancedClassLoaderProvider.java | 2 +- .../FactoryClassLoader.java | 2 +- .../CoreTypeConverter.java} | 8 +- .../ParameterConverter.java} | 39 ++--- .../worker/handler/WorkerWarmupHandler.java | 2 +- .../worker/invoker/MethodInvoker.java | 34 ++++ .../worker/broker/JavaFunctionBrokerTest.java | 2 +- ...rTest.java => ParameterConverterTest.java} | 157 +++++++++--------- ...erTest.java => CoreTypeConverterTest.java} | 20 +-- .../broker/tests/FunctionDefinitionTest.java | 2 +- ...onEnvironmentReloadRequestHandlerTest.java | 2 +- 22 files changed, 192 insertions(+), 238 deletions(-) delete mode 100644 src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodInvokeInfo.java rename src/main/java/com/microsoft/azure/functions/worker/{reflect => classloader}/ClassLoaderProvider.java (89%) rename src/main/java/com/microsoft/azure/functions/worker/{reflect => classloader}/DefaultClassLoaderProvider.java (97%) rename src/main/java/com/microsoft/azure/functions/worker/{reflect => classloader}/EnhancedClassLoaderProvider.java (97%) rename src/main/java/com/microsoft/azure/functions/worker/{reflect => classloader}/FactoryClassLoader.java (84%) rename src/main/java/com/microsoft/azure/functions/worker/{broker/CoreTypeResolver.java => converter/CoreTypeConverter.java} (95%) rename src/main/java/com/microsoft/azure/functions/worker/{broker/ParameterResolver.java => converter/ParameterConverter.java} (62%) create mode 100644 src/main/java/com/microsoft/azure/functions/worker/invoker/MethodInvoker.java rename src/test/java/com/microsoft/azure/functions/worker/broker/{ParameterResolverTest.java => ParameterConverterTest.java} (93%) rename src/test/java/com/microsoft/azure/functions/worker/broker/tests/{CoreTypeResolverTest.java => CoreTypeConverterTest.java} (86%) diff --git a/endtoendtests/pom.xml b/endtoendtests/pom.xml index 755c550e..4b77dd57 100644 --- a/endtoendtests/pom.xml +++ b/endtoendtests/pom.xml @@ -22,7 +22,7 @@ UTF-8 1.8 1.18.0 - 3.0.0-SNAPSHOT + 3.0.0 1.0.0-beta.1 azure-functions-java-endtoendtests westus diff --git a/src/main/java/com/microsoft/azure/functions/worker/JavaWorkerClient.java b/src/main/java/com/microsoft/azure/functions/worker/JavaWorkerClient.java index 2073b2a2..b54d94c6 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/JavaWorkerClient.java +++ b/src/main/java/com/microsoft/azure/functions/worker/JavaWorkerClient.java @@ -12,7 +12,7 @@ import com.microsoft.azure.functions.worker.broker.*; import com.microsoft.azure.functions.worker.handler.*; -import com.microsoft.azure.functions.worker.reflect.*; +import com.microsoft.azure.functions.worker.classloader.*; import com.microsoft.azure.functions.rpc.messages.*; /** diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java index 3ab1637b..5b18f4d7 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java @@ -6,12 +6,11 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.UUID; import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.rpc.messages.ParameterBinding; import com.microsoft.azure.functions.rpc.messages.TypedData; -import com.microsoft.azure.functions.worker.broker.CoreTypeResolver; +import com.microsoft.azure.functions.worker.converter.CoreTypeConverter; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -21,16 +20,22 @@ * Thread-safety: Single thread. */ public final class BindingDataStore { + + private final Map targets; + private final Map> inputSources; + private final Map> otherSources; + private final Map> metadataSources; + private Map definitions; + public static final String RETURN_NAME = "$return"; + public BindingDataStore() { this.targets = new HashMap<>(); this.inputSources = new HashMap<>(); this.otherSources = new HashMap<>(); this.metadataSources = new HashMap<>(); - this.promotedTargets = null; } - ///////////////////////// region Input Binding Data - + //Logics for input Binding Data public void addParameterSources(List parameters) { assert parameters != null; for (ParameterBinding parameter : parameters) { @@ -92,24 +97,21 @@ private static DataSource rpcSourceFromParameter(ParameterBinding parameter) return rpcSourceFromTypedData(parameter.getName(), parameter.getData()); } - ///////////////////////// end region Input Binding Data - - ///////////////////////// region Output Binding Data - public List getOutputParameterBindings(boolean excludeReturn) throws Exception { + //Logics for output Binding Data + public List getOutputParameterBindings() throws Exception { List bindings = new ArrayList<>(); - for (Map.Entry entry : this.getTarget(this.promotedTargets).entrySet()) { - if (!excludeReturn || !entry.getKey().equals(RETURN_NAME)) { - entry.getValue().computeFromValue().ifPresent(data -> - bindings.add(ParameterBinding.newBuilder().setName(entry.getKey()).setData(data).build()) - ); - } + for (String key : this.targets.keySet()) { + if (key.equals(RETURN_NAME)) continue; + DataTarget dataTarget = this.targets.get(key); + dataTarget.computeFromValue().ifPresent(data -> + bindings.add(ParameterBinding.newBuilder().setName(key).setData(data).build())); } return bindings; } public Optional getDataTargetTypedValue(String name) throws Exception{ - return Optional.ofNullable(this.getTarget(this.promotedTargets).get(name)).map(o -> { + return Optional.ofNullable(this.targets.get(name)).map(o -> { try { return o.computeFromValue().orElse(null); } catch (Exception ex) { @@ -119,48 +121,37 @@ public Optional getDataTargetTypedValue(String name) throws Exception }); } - public Optional getOrAddDataTarget(UUID outputId, String name, Type target, boolean hasImplicitOutput) { + public Optional getOrAddDataTarget(String name, Type target, boolean hasImplicitOutput) { DataTarget output = null; if (this.isDataTargetValid(name, target)) { - output = this.getTarget(outputId).get(name); + output = this.targets.get(name); if (output == null && (this.isDefinitionOutput(name) || hasImplicitOutput)) { - this.getTarget(outputId).put(name, output = rpcDataTargetFromType(target)); + this.targets.put(name, output = rpcDataTargetFromType(target)); } } - return Optional.ofNullable(output).map(out -> new BindingData(out)); + return Optional.ofNullable(output).map(BindingData::new); } public void setDataTargetValue(String name, Object value) { - Optional.ofNullable(this.getTarget(this.promotedTargets).get(name)).ifPresent(out -> out.setValue(value)); - } - - public void promoteDataTargets(UUID outputId) { - this.promotedTargets = outputId; - } - - private Map getTarget(UUID outputId) { - return this.targets.computeIfAbsent(outputId, m -> new HashMap<>()); + Optional.ofNullable(this.targets.get(name)).ifPresent(out -> out.setValue(value)); } private boolean isDataTargetValid(String name, Type target) { if (!name.equals(RETURN_NAME)) { - if (!CoreTypeResolver.isValidOutputType(target)) { return false; } - target = CoreTypeResolver.getParameterizedActualTypeArgumentsType(target); + if (!CoreTypeConverter.isValidOutputType(target)) { return false; } + target = CoreTypeConverter.getParameterizedActualTypeArgumentsType(target); } return true; } private static DataTarget rpcDataTargetFromType(Type target) { - if (CoreTypeResolver.isHttpResponse(target)) { + if (CoreTypeConverter.isHttpResponse(target)) { return new RpcHttpDataTarget(); } return new RpcUnspecifiedDataTarget(); } - ///////////////////////// end region Output Binding Data - - ///////////////////////// region Binding Definitions - + //Logics for binding Definitions public void setBindingDefinitions(Map definitions) { this.definitions = definitions; } @@ -172,14 +163,4 @@ private boolean isDefinitionOutput(String name) { private Optional getDefinition(String name) { return Optional.ofNullable(this.definitions.get(name)); } - - ///////////////////////// endregion Binding Definitions - - private UUID promotedTargets; - private final Map> targets; - private final Map> inputSources; - private final Map> otherSources; - private final Map> metadataSources; - private Map definitions; - public static final String RETURN_NAME = "$return"; } diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/DataOperations.java b/src/main/java/com/microsoft/azure/functions/worker/binding/DataOperations.java index 3164c0bb..a5c28e9a 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/binding/DataOperations.java +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/DataOperations.java @@ -1,10 +1,7 @@ package com.microsoft.azure.functions.worker.binding; -import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -15,13 +12,11 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.reflect.TypeUtils; -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.*; import com.microsoft.azure.functions.worker.WorkerLogManager; -import com.microsoft.azure.functions.worker.broker.CoreTypeResolver; +import com.microsoft.azure.functions.worker.converter.CoreTypeConverter; @FunctionalInterface interface CheckedFunction { @@ -101,7 +96,7 @@ Optional apply(T sourceValue, Type targetType) { // Try POJO if (Collection.class.isAssignableFrom(TypeUtils.getRawType(targetType, null))) { - Class collectionItemType = (Class) CoreTypeResolver + Class collectionItemType = (Class) CoreTypeConverter .getParameterizedActualTypeArgumentsType(targetType); try { @@ -185,7 +180,7 @@ static Object generalAssignment(Object value, Type target) { if (value == null) { return ObjectUtils.NULL; } - if (CoreTypeResolver.getRuntimeClass(target).isAssignableFrom(value.getClass())) { + if (CoreTypeConverter.getRuntimeClass(target).isAssignableFrom(value.getClass())) { return value; } throw new ClassCastException("Cannot convert " + value + "to type " + target.getTypeName()); diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/FunctionDefinition.java b/src/main/java/com/microsoft/azure/functions/worker/broker/FunctionDefinition.java index eb42c200..f5051a13 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/FunctionDefinition.java +++ b/src/main/java/com/microsoft/azure/functions/worker/broker/FunctionDefinition.java @@ -3,7 +3,7 @@ import com.microsoft.azure.functions.rpc.messages.BindingInfo; import com.microsoft.azure.functions.worker.binding.BindingDefinition; import com.microsoft.azure.functions.worker.description.FunctionMethodDescriptor; -import com.microsoft.azure.functions.worker.reflect.ClassLoaderProvider; +import com.microsoft.azure.functions.worker.classloader.ClassLoaderProvider; import java.lang.reflect.Method; import java.util.ArrayList; diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java index a855e7c9..cd24a662 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java +++ b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java @@ -19,7 +19,7 @@ import com.microsoft.azure.functions.worker.chain.FunctionExecutionMiddleware; import com.microsoft.azure.functions.worker.chain.InvocationChainFactory; import com.microsoft.azure.functions.worker.description.FunctionMethodDescriptor; -import com.microsoft.azure.functions.worker.reflect.ClassLoaderProvider; +import com.microsoft.azure.functions.worker.classloader.ClassLoaderProvider; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -122,7 +122,7 @@ public Optional invokeMethod(String id, InvocationRequest request, Li throws Exception { ExecutionContextDataSource executionContextDataSource = buildExecutionContext(id, request); invoke(executionContextDataSource); - outputs.addAll(executionContextDataSource.getDataStore().getOutputParameterBindings(true)); + outputs.addAll(executionContextDataSource.getDataStore().getOutputParameterBindings()); return executionContextDataSource.getDataStore().getDataTargetTypedValue(BindingDataStore.RETURN_NAME); } diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutor.java b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutor.java index 759f575f..03585d88 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutor.java +++ b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutor.java @@ -1,6 +1,7 @@ package com.microsoft.azure.functions.worker.broker; import com.microsoft.azure.functions.worker.binding.*; +import com.microsoft.azure.functions.worker.converter.ParameterConverter; /** * Used to executor of arbitrary Java method in any JAR using reflection. @@ -17,9 +18,9 @@ public static JavaMethodExecutor getInstance(){ private JavaMethodExecutor() {} public void execute(ExecutionContextDataSource executionContextDataSource) throws Exception { - Object retValue = ParameterResolver.resolveArguments(executionContextDataSource) + Object retValue = ParameterConverter.resolveArguments(executionContextDataSource) .orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input")) - .invoke(executionContextDataSource::getFunctionInstance); + .invoke(executionContextDataSource.getFunctionInstance()); executionContextDataSource.updateReturnValue(retValue); } } diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodInvokeInfo.java b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodInvokeInfo.java deleted file mode 100644 index a60615ef..00000000 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodInvokeInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.microsoft.azure.functions.worker.broker; - -import java.lang.reflect.*; -import java.util.*; - -import org.apache.commons.lang3.exception.*; - -interface InstanceSupplier { - Object get() throws Exception; -} - -/** - * Used to run the actual method with specific arguments using reflection. - * Thread-Safety: Single thread. - */ -class JavaMethodInvokeInfo { - private JavaMethodInvokeInfo() {} - - Object invoke(InstanceSupplier instanceSupplier) throws Exception { - Object instance = Modifier.isStatic(this.m.getModifiers()) ? null : instanceSupplier.get(); - try { - return this.m.invoke(instance, this.args); - } catch (Exception ex) { - return ExceptionUtils.rethrow(ex); - } - } - - private Method m; - private Object[] args; - - static class Builder { - Builder() { - this.info = new JavaMethodInvokeInfo(); - this.arguments = new LinkedList<>(); - } - - JavaMethodInvokeInfo build() { - assert this.info.m != null; - this.info.args = this.arguments.toArray(); - return this.info; - } - - void setMethod(Method method) { this.info.m = method; } - void appendArgument(Object argument) { this.arguments.add(argument); } - - private JavaMethodInvokeInfo info; - private List arguments; - } -} diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/ParamBindInfo.java b/src/main/java/com/microsoft/azure/functions/worker/broker/ParamBindInfo.java index a0c2d12d..afc7f5bd 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/ParamBindInfo.java +++ b/src/main/java/com/microsoft/azure/functions/worker/broker/ParamBindInfo.java @@ -1,5 +1,7 @@ package com.microsoft.azure.functions.worker.broker; +import com.microsoft.azure.functions.worker.converter.CoreTypeConverter; + import java.lang.reflect.Parameter; import java.lang.reflect.Type; @@ -11,10 +13,10 @@ public final class ParamBindInfo { private final boolean isImplicitOutput; private final Parameter parameter; ParamBindInfo(Parameter param) { - this.name = CoreTypeResolver.getAnnotationName(param); + this.name = CoreTypeConverter.getAnnotationName(param); this.type = param.getParameterizedType(); - this.bindingNameAnnotation = CoreTypeResolver.getBindingNameAnnotation(param); - this.isImplicitOutput = CoreTypeResolver.checkImplicitOutput(param); + this.bindingNameAnnotation = CoreTypeConverter.getBindingNameAnnotation(param); + this.isImplicitOutput = CoreTypeConverter.checkImplicitOutput(param); this.parameter = param; } diff --git a/src/main/java/com/microsoft/azure/functions/worker/reflect/ClassLoaderProvider.java b/src/main/java/com/microsoft/azure/functions/worker/classloader/ClassLoaderProvider.java similarity index 89% rename from src/main/java/com/microsoft/azure/functions/worker/reflect/ClassLoaderProvider.java rename to src/main/java/com/microsoft/azure/functions/worker/classloader/ClassLoaderProvider.java index 86ed9999..08ac74b3 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/reflect/ClassLoaderProvider.java +++ b/src/main/java/com/microsoft/azure/functions/worker/classloader/ClassLoaderProvider.java @@ -1,4 +1,4 @@ -package com.microsoft.azure.functions.worker.reflect; +package com.microsoft.azure.functions.worker.classloader; import java.io.*; import java.net.*; diff --git a/src/main/java/com/microsoft/azure/functions/worker/reflect/DefaultClassLoaderProvider.java b/src/main/java/com/microsoft/azure/functions/worker/classloader/DefaultClassLoaderProvider.java similarity index 97% rename from src/main/java/com/microsoft/azure/functions/worker/reflect/DefaultClassLoaderProvider.java rename to src/main/java/com/microsoft/azure/functions/worker/classloader/DefaultClassLoaderProvider.java index 9d64b3c9..9311ab4a 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/reflect/DefaultClassLoaderProvider.java +++ b/src/main/java/com/microsoft/azure/functions/worker/classloader/DefaultClassLoaderProvider.java @@ -1,4 +1,4 @@ -package com.microsoft.azure.functions.worker.reflect; +package com.microsoft.azure.functions.worker.classloader; import java.io.*; import java.lang.reflect.*; diff --git a/src/main/java/com/microsoft/azure/functions/worker/reflect/EnhancedClassLoaderProvider.java b/src/main/java/com/microsoft/azure/functions/worker/classloader/EnhancedClassLoaderProvider.java similarity index 97% rename from src/main/java/com/microsoft/azure/functions/worker/reflect/EnhancedClassLoaderProvider.java rename to src/main/java/com/microsoft/azure/functions/worker/classloader/EnhancedClassLoaderProvider.java index 40cc499d..d5800e6f 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/reflect/EnhancedClassLoaderProvider.java +++ b/src/main/java/com/microsoft/azure/functions/worker/classloader/EnhancedClassLoaderProvider.java @@ -1,4 +1,4 @@ -package com.microsoft.azure.functions.worker.reflect; +package com.microsoft.azure.functions.worker.classloader; import java.io.*; import java.net.*; diff --git a/src/main/java/com/microsoft/azure/functions/worker/reflect/FactoryClassLoader.java b/src/main/java/com/microsoft/azure/functions/worker/classloader/FactoryClassLoader.java similarity index 84% rename from src/main/java/com/microsoft/azure/functions/worker/reflect/FactoryClassLoader.java rename to src/main/java/com/microsoft/azure/functions/worker/classloader/FactoryClassLoader.java index dcb5c869..9457e453 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/reflect/FactoryClassLoader.java +++ b/src/main/java/com/microsoft/azure/functions/worker/classloader/FactoryClassLoader.java @@ -1,4 +1,4 @@ -package com.microsoft.azure.functions.worker.reflect; +package com.microsoft.azure.functions.worker.classloader; import org.apache.commons.lang3.SystemUtils; diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/CoreTypeResolver.java b/src/main/java/com/microsoft/azure/functions/worker/converter/CoreTypeConverter.java similarity index 95% rename from src/main/java/com/microsoft/azure/functions/worker/broker/CoreTypeResolver.java rename to src/main/java/com/microsoft/azure/functions/worker/converter/CoreTypeConverter.java index 08383c16..ee5b7bda 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/CoreTypeResolver.java +++ b/src/main/java/com/microsoft/azure/functions/worker/converter/CoreTypeConverter.java @@ -1,4 +1,4 @@ -package com.microsoft.azure.functions.worker.broker; +package com.microsoft.azure.functions.worker.converter; import java.lang.annotation.*; import java.lang.reflect.*; @@ -6,7 +6,7 @@ import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.worker.Constants; -public class CoreTypeResolver { +public class CoreTypeConverter { private static boolean isOutputParameter(Type target) { if (target instanceof ParameterizedType) { target = ((ParameterizedType) target).getRawType(); @@ -108,7 +108,7 @@ private static String getBindingNameFromCustomBindingAnnotation(Annotation custo } } - static String getBindingNameAnnotation(Parameter param) { + public static String getBindingNameAnnotation(Parameter param) { Annotation bindingNameAnnotation = null; for (Annotation item : param.getAnnotations()){ if (item.annotationType().getName().equals("com.microsoft.azure.functions.annotation.BindingName")){ @@ -129,7 +129,7 @@ static String getBindingNameAnnotation(Parameter param) { return new String(""); } - static boolean checkImplicitOutput(Parameter parameter) { + public static boolean checkImplicitOutput(Parameter parameter) { Annotation[] annotations = parameter.getAnnotations(); for (Annotation annotation : annotations) { for (Annotation item : annotation.annotationType().getAnnotations()) { diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/ParameterResolver.java b/src/main/java/com/microsoft/azure/functions/worker/converter/ParameterConverter.java similarity index 62% rename from src/main/java/com/microsoft/azure/functions/worker/broker/ParameterResolver.java rename to src/main/java/com/microsoft/azure/functions/worker/converter/ParameterConverter.java index e4b605b0..ad135b25 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/ParameterResolver.java +++ b/src/main/java/com/microsoft/azure/functions/worker/converter/ParameterConverter.java @@ -1,11 +1,13 @@ -package com.microsoft.azure.functions.worker.broker; +package com.microsoft.azure.functions.worker.converter; import java.lang.invoke.WrongMethodTypeException; import java.lang.reflect.Type; import java.util.Optional; -import java.util.UUID; import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; +import com.microsoft.azure.functions.worker.broker.MethodBindInfo; +import com.microsoft.azure.functions.worker.broker.ParamBindInfo; +import com.microsoft.azure.functions.worker.invoker.MethodInvoker; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.reflect.TypeUtils; @@ -17,28 +19,24 @@ * Resolve a Java method overload using reflection. * Thread-Safety: Multiple thread. */ -public class ParameterResolver { - public static Optional resolveArguments(ExecutionContextDataSource executionContextDataSource) { - InvokeInfoBuilder invoker = resolve(executionContextDataSource); - if (invoker != null) { - executionContextDataSource.getDataStore().promoteDataTargets(invoker.getOutputsId()); - return Optional.of(invoker.build()); - } - return Optional.empty(); +public class ParameterConverter { + public static Optional resolveArguments(ExecutionContextDataSource executionContextDataSource) { + MethodInvoker invoker = resolve(executionContextDataSource); + return invoker != null ? Optional.of(invoker) : Optional.empty(); } - private static InvokeInfoBuilder resolve(ExecutionContextDataSource executionContextDataSource) { + private static MethodInvoker resolve(ExecutionContextDataSource executionContextDataSource) { try { MethodBindInfo method = executionContextDataSource.getMethodBindInfo(); BindingDataStore dataStore = executionContextDataSource.getDataStore(); - final InvokeInfoBuilder invokeInfo = new InvokeInfoBuilder(method); + final MethodInvoker invoker = new MethodInvoker(method.getMethod()); for (ParamBindInfo param : method.getParams()) { String paramName = param.getName(); Type paramType = param.getType(); String paramBindingNameAnnotation = param.getBindingNameAnnotation(); Optional argument; if (OutputBinding.class.isAssignableFrom(TypeUtils.getRawType(paramType, null))) { - argument = dataStore.getOrAddDataTarget(invokeInfo.getOutputsId(), paramName, paramType, false); + argument = dataStore.getOrAddDataTarget(paramName, paramType, false); } else if (paramName != null && !paramName.isEmpty()) { argument = executionContextDataSource.getBindingData(paramName, paramType); @@ -50,28 +48,19 @@ else if (paramName == null && !paramBindingNameAnnotation.isEmpty()) { argument = dataStore.getDataByType(paramType); } BindingData actualArg = argument.orElseThrow(WrongMethodTypeException::new); - invokeInfo.appendArgument(actualArg.getValue()); + invoker.addArgument(actualArg.getValue()); } // For function annotated with @HasImplicitOutput, we should allow it to send back data even function's return type is void // Reference to https://github.com/microsoft/durabletask-java/issues/126 if (!method.getMethod().getReturnType().equals(void.class) && !method.getMethod().getReturnType().equals(Void.class) || method.hasImplicitOutput()) { - dataStore.getOrAddDataTarget(invokeInfo.getOutputsId(), BindingDataStore.RETURN_NAME, method.getMethod().getReturnType(), method.hasImplicitOutput()); + dataStore.getOrAddDataTarget(BindingDataStore.RETURN_NAME, method.getMethod().getReturnType(), method.hasImplicitOutput()); } - return invokeInfo; + return invoker; } catch (Exception ex) { ExceptionUtils.rethrow(ex); return null; } } - - public static final class InvokeInfoBuilder extends JavaMethodInvokeInfo.Builder { - private final UUID outputsId = UUID.randomUUID(); - public InvokeInfoBuilder(MethodBindInfo method) { super.setMethod(method.getMethod()); } - - public UUID getOutputsId() { - return outputsId; - } - } } diff --git a/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerWarmupHandler.java b/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerWarmupHandler.java index 620fd5ad..86df10cf 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerWarmupHandler.java +++ b/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerWarmupHandler.java @@ -3,7 +3,7 @@ import com.microsoft.azure.functions.rpc.messages.*; import com.microsoft.azure.functions.worker.WorkerLogManager; import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker; -import com.microsoft.azure.functions.worker.reflect.FactoryClassLoader; +import com.microsoft.azure.functions.worker.classloader.FactoryClassLoader; import java.util.*; diff --git a/src/main/java/com/microsoft/azure/functions/worker/invoker/MethodInvoker.java b/src/main/java/com/microsoft/azure/functions/worker/invoker/MethodInvoker.java new file mode 100644 index 00000000..bd1af20f --- /dev/null +++ b/src/main/java/com/microsoft/azure/functions/worker/invoker/MethodInvoker.java @@ -0,0 +1,34 @@ +package com.microsoft.azure.functions.worker.invoker; + +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.exception.*; + +/** + * Used to run the actual method with specific arguments using reflection. + * Thread-Safety: Single thread. + */ +public class MethodInvoker { + private final Method method; + private final List arguments; + + public MethodInvoker(Method method) { + this.method = method; + this.arguments = new ArrayList<>(); + } + + public void addArgument(Object argument) { + this.arguments.add(argument); + } + + public Object invoke(Object instance) throws Exception { + instance = Modifier.isStatic(this.method.getModifiers()) ? null : instance; + try { + return this.method.invoke(instance, this.arguments.toArray()); + } catch (Exception ex) { + return ExceptionUtils.rethrow(ex); + } + } +} diff --git a/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java b/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java index 1084d0d5..a0c6ce1a 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java @@ -3,7 +3,7 @@ import com.microsoft.azure.functions.rpc.messages.InvocationRequest; import com.microsoft.azure.functions.rpc.messages.ParameterBinding; import com.microsoft.azure.functions.rpc.messages.TypedData; -import com.microsoft.azure.functions.worker.reflect.DefaultClassLoaderProvider; +import com.microsoft.azure.functions.worker.classloader.DefaultClassLoaderProvider; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; diff --git a/src/test/java/com/microsoft/azure/functions/worker/broker/ParameterResolverTest.java b/src/test/java/com/microsoft/azure/functions/worker/broker/ParameterConverterTest.java similarity index 93% rename from src/test/java/com/microsoft/azure/functions/worker/broker/ParameterResolverTest.java rename to src/test/java/com/microsoft/azure/functions/worker/broker/ParameterConverterTest.java index 385edade..138445a0 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/broker/ParameterResolverTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/broker/ParameterConverterTest.java @@ -1,78 +1,79 @@ -package com.microsoft.azure.functions.worker.broker; - -import com.microsoft.azure.functions.rpc.messages.RpcException; -import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; -import com.microsoft.azure.functions.worker.WorkerLogManager; -import com.microsoft.azure.functions.worker.binding.BindingDataStore; -import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; -import com.microsoft.azure.functions.worker.binding.ExecutionRetryContext; -import com.microsoft.azure.functions.worker.binding.ExecutionTraceContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Logger; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - - -@ExtendWith(MockitoExtension.class) -public class ParameterResolverTest { - - private ExecutionContextDataSource executionContextDataSource; - @Mock - private MethodBindInfo methodBindInfo; - - @BeforeEach - public void setup() { - String invocationId = "testInvocationId"; - ExecutionTraceContext traceContext = new ExecutionTraceContext("traceParent", "traceState", new HashMap<>()); - ExecutionRetryContext retryContext = new ExecutionRetryContext(1, 2, RpcException.newBuilder().build()); - String functionName = "ParameterResolverTest"; - BindingDataStore dataStore = new BindingDataStore(); - dataStore.setBindingDefinitions(new HashMap<>()); - try (MockedStatic workerLogManagerMockedStatic = Mockito.mockStatic(WorkerLogManager.class)) { - workerLogManagerMockedStatic.when(() -> WorkerLogManager.getInvocationLogger(invocationId)) - .thenReturn(Logger.getAnonymousLogger()); - executionContextDataSource = new ExecutionContextDataSource(invocationId, - traceContext, retryContext, functionName, dataStore, methodBindInfo, - this.getClass(), new ArrayList<>(), new FunctionInstanceInjector() { - @Override - public T getInstance(Class functionClass) throws Exception { - return null; - } - }); - } - - } - - @Test - public void testResolveArgumentsHasImplicitOutputTrue() throws Exception { - Method testMethod = this.getClass().getDeclaredMethod("testMethod"); - when(methodBindInfo.hasImplicitOutput()).thenReturn(true); - when(methodBindInfo.getMethod()).thenReturn(testMethod); - when(methodBindInfo.getParams()).thenReturn(new ArrayList<>()); - ParameterResolver.resolveArguments(executionContextDataSource); - assertTrue(executionContextDataSource.getDataStore().getDataTargetTypedValue(BindingDataStore.RETURN_NAME).isPresent()); - } - - @Test - public void testResolveArgumentsHasImplicitOutputFalse() throws Exception { - Method testMethod = this.getClass().getDeclaredMethod("testMethod"); - when(methodBindInfo.hasImplicitOutput()).thenReturn(false); - when(methodBindInfo.getMethod()).thenReturn(testMethod); - when(methodBindInfo.getParams()).thenReturn(new ArrayList<>()); - ParameterResolver.resolveArguments(executionContextDataSource); - assertFalse(executionContextDataSource.getDataStore().getDataTargetTypedValue(BindingDataStore.RETURN_NAME).isPresent()); - } - - public void testMethod() {} -} +package com.microsoft.azure.functions.worker.broker; + +import com.microsoft.azure.functions.rpc.messages.RpcException; +import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; +import com.microsoft.azure.functions.worker.WorkerLogManager; +import com.microsoft.azure.functions.worker.binding.BindingDataStore; +import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; +import com.microsoft.azure.functions.worker.binding.ExecutionRetryContext; +import com.microsoft.azure.functions.worker.binding.ExecutionTraceContext; +import com.microsoft.azure.functions.worker.converter.ParameterConverter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +public class ParameterConverterTest { + + private ExecutionContextDataSource executionContextDataSource; + @Mock + private MethodBindInfo methodBindInfo; + + @BeforeEach + public void setup() { + String invocationId = "testInvocationId"; + ExecutionTraceContext traceContext = new ExecutionTraceContext("traceParent", "traceState", new HashMap<>()); + ExecutionRetryContext retryContext = new ExecutionRetryContext(1, 2, RpcException.newBuilder().build()); + String functionName = "ParameterResolverTest"; + BindingDataStore dataStore = new BindingDataStore(); + dataStore.setBindingDefinitions(new HashMap<>()); + try (MockedStatic workerLogManagerMockedStatic = Mockito.mockStatic(WorkerLogManager.class)) { + workerLogManagerMockedStatic.when(() -> WorkerLogManager.getInvocationLogger(invocationId)) + .thenReturn(Logger.getAnonymousLogger()); + executionContextDataSource = new ExecutionContextDataSource(invocationId, + traceContext, retryContext, functionName, dataStore, methodBindInfo, + this.getClass(), new ArrayList<>(), new FunctionInstanceInjector() { + @Override + public T getInstance(Class functionClass) throws Exception { + return null; + } + }); + } + + } + + @Test + public void testResolveArgumentsHasImplicitOutputTrue() throws Exception { + Method testMethod = this.getClass().getDeclaredMethod("testMethod"); + when(methodBindInfo.hasImplicitOutput()).thenReturn(true); + when(methodBindInfo.getMethod()).thenReturn(testMethod); + when(methodBindInfo.getParams()).thenReturn(new ArrayList<>()); + ParameterConverter.resolveArguments(executionContextDataSource); + assertTrue(executionContextDataSource.getDataStore().getDataTargetTypedValue(BindingDataStore.RETURN_NAME).isPresent()); + } + + @Test + public void testResolveArgumentsHasImplicitOutputFalse() throws Exception { + Method testMethod = this.getClass().getDeclaredMethod("testMethod"); + when(methodBindInfo.hasImplicitOutput()).thenReturn(false); + when(methodBindInfo.getMethod()).thenReturn(testMethod); + when(methodBindInfo.getParams()).thenReturn(new ArrayList<>()); + ParameterConverter.resolveArguments(executionContextDataSource); + assertFalse(executionContextDataSource.getDataStore().getDataTargetTypedValue(BindingDataStore.RETURN_NAME).isPresent()); + } + + public void testMethod() {} +} diff --git a/src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeResolverTest.java b/src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeConverterTest.java similarity index 86% rename from src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeResolverTest.java rename to src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeConverterTest.java index 3e8dbc22..849b27df 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeResolverTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/broker/tests/CoreTypeConverterTest.java @@ -5,16 +5,16 @@ import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.annotation.*; -import com.microsoft.azure.functions.worker.broker.CoreTypeResolver; +import com.microsoft.azure.functions.worker.converter.CoreTypeConverter; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; -import static com.microsoft.azure.functions.worker.broker.CoreTypeResolver.getRuntimeClass; -import static com.microsoft.azure.functions.worker.broker.CoreTypeResolver.isValidOutputType; +import static com.microsoft.azure.functions.worker.converter.CoreTypeConverter.getRuntimeClass; +import static com.microsoft.azure.functions.worker.converter.CoreTypeConverter.isValidOutputType; import static org.junit.jupiter.api.Assertions.*; -public class CoreTypeResolverTest { +public class CoreTypeConverterTest { public void CustomBinding_Valid( @HttpTrigger(name = "req", methods = { HttpMethod.GET, @@ -64,7 +64,7 @@ public void getNameFromCustomBinding() { Method customBindingValid = getFunctionMethod("CustomBinding_Valid"); Parameter[] parameters = customBindingValid.getParameters(); for (Parameter parameter : parameters) { - String annotationName = CoreTypeResolver.getAnnotationName(parameter); + String annotationName = CoreTypeConverter.getAnnotationName(parameter); assertNotNull(annotationName); } } @@ -74,7 +74,7 @@ public void getNameFromTestCustomBinding() { Method customBindingValid = getFunctionMethod("TestCustomBindingName_OverridesCustomBindingName"); Parameter[] parameters = customBindingValid.getParameters(); for (Parameter parameter : parameters) { - String annotationName = CoreTypeResolver.getAnnotationName(parameter); + String annotationName = CoreTypeConverter.getAnnotationName(parameter); assertNotNull(annotationName); assertTrue(StringUtils.isNotEmpty(annotationName)); } @@ -85,7 +85,7 @@ public void getNameFromCustomBinding_ReturnsNull() { Method customBindingInvalid = getFunctionMethod("CustomBinding_Invalid"); Parameter[] parameters = customBindingInvalid.getParameters(); for (Parameter parameter : parameters) { - String annotationName = CoreTypeResolver.getAnnotationName(parameter); + String annotationName = CoreTypeConverter.getAnnotationName(parameter); assertNull(annotationName); } } @@ -123,7 +123,7 @@ private OutputBinding> outputExoutputInteg } private Type returnTypeOf(String type) throws NoSuchMethodException { - Method m = CoreTypeResolverTest.class.getDeclaredMethod(type); + Method m = CoreTypeConverterTest.class.getDeclaredMethod(type); m.setAccessible(true); return m.getGenericReturnType(); } @@ -151,8 +151,8 @@ public void setValue(T value) { } private Method getFunctionMethod(String methodName) { - CoreTypeResolverTest coreTypeResolverTest = new CoreTypeResolverTest(); - Class testsClass = coreTypeResolverTest.getClass(); + CoreTypeConverterTest coreTypeResolverTest = new CoreTypeConverterTest(); + Class testsClass = coreTypeResolverTest.getClass(); Method[] methods = testsClass.getMethods(); Method functionMethod = null; for (Method method : methods) { diff --git a/src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTest.java b/src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTest.java index 1b97e3a4..e4b902f0 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTest.java @@ -5,7 +5,7 @@ import com.microsoft.azure.functions.rpc.messages.*; import com.microsoft.azure.functions.worker.broker.*; import com.microsoft.azure.functions.worker.description.*; -import com.microsoft.azure.functions.worker.reflect.*; +import com.microsoft.azure.functions.worker.classloader.*; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/microsoft/azure/functions/worker/handler/tests/FunctionEnvironmentReloadRequestHandlerTest.java b/src/test/java/com/microsoft/azure/functions/worker/handler/tests/FunctionEnvironmentReloadRequestHandlerTest.java index 041d800b..2beeab46 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/handler/tests/FunctionEnvironmentReloadRequestHandlerTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/handler/tests/FunctionEnvironmentReloadRequestHandlerTest.java @@ -7,7 +7,7 @@ import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker; import com.microsoft.azure.functions.worker.handler.FunctionEnvironmentReloadRequestHandler; -import com.microsoft.azure.functions.worker.reflect.DefaultClassLoaderProvider; +import com.microsoft.azure.functions.worker.classloader.DefaultClassLoaderProvider; import org.junit.jupiter.api.Test; public class FunctionEnvironmentReloadRequestHandlerTest {