diff --git a/pom.xml b/pom.xml index aaf2720a..2f9b27b5 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.8 1.8 1.2.0 - 1.0.0 + 1.1.0 2.2.0 5.9.1 4.11.0 diff --git a/src/main/java/com/microsoft/azure/functions/worker/Util.java b/src/main/java/com/microsoft/azure/functions/worker/Util.java index 2b083ed3..0adffe42 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/Util.java +++ b/src/main/java/com/microsoft/azure/functions/worker/Util.java @@ -1,6 +1,11 @@ package com.microsoft.azure.functions.worker; +import com.google.gson.Gson; +import com.microsoft.azure.functions.spi.inject.GsonInstanceInjector; + public class Util { + private static Gson gsonInstance; + private static final Object utilLock = new Object(); public static boolean isTrue(String value) { if(value != null && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("1"))) { return true; @@ -11,4 +16,13 @@ public static boolean isTrue(String value) { public static String getJavaVersion() { return String.join(" - ", System.getProperty("java.home"), System.getProperty("java.version")); } + + public static void setGsonInstance(Gson instance) { + gsonInstance = instance; + } + + public static Gson getGsonInstance() { + return gsonInstance; + } + } \ No newline at end of file diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java index 0d3c2d10..88fea7e1 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.List; +import com.microsoft.azure.functions.worker.Util; import org.apache.commons.lang3.reflect.TypeUtils; import com.google.gson.Gson; @@ -17,7 +18,7 @@ public RpcJsonDataSource(String name, String value) { super(name, value, JSON_DATA_OPERATIONS); } - public static final Gson gson = new Gson(); + public static final Gson gson = Util.getGsonInstance(); public static final JsonParser gsonParser = new JsonParser(); private static final DataOperations JSON_DATA_OPERATIONS = new DataOperations<>(); 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 9b72f76d..d6021e87 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 @@ -7,10 +7,13 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import com.google.gson.Gson; import com.microsoft.azure.functions.internal.spi.middleware.Middleware; import com.microsoft.azure.functions.rpc.messages.*; import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; +import com.microsoft.azure.functions.spi.inject.GsonInstanceInjector; import com.microsoft.azure.functions.worker.Constants; +import com.microsoft.azure.functions.worker.Util; import com.microsoft.azure.functions.worker.WorkerLogManager; import com.microsoft.azure.functions.worker.binding.BindingDataStore; import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; @@ -38,7 +41,7 @@ public class JavaFunctionBroker { private volatile FunctionInstanceInjector functionInstanceInjector; private final Object oneTimeLogicInitializationLock = new Object(); - private FunctionInstanceInjector newInstanceInjector() { + private FunctionInstanceInjector newFunctionInstanceInjector() { return new FunctionInstanceInjector() { @Override public T getInstance(Class functionClass) throws Exception { @@ -53,7 +56,7 @@ public JavaFunctionBroker(ClassLoaderProvider classLoaderProvider) { } public void loadMethod(FunctionMethodDescriptor descriptor, Map bindings) - throws ClassNotFoundException, NoSuchMethodException, IOException { + throws Exception { descriptor.validate(); addSearchPathsToClassLoader(descriptor); initializeOneTimeLogics(); @@ -61,12 +64,12 @@ public void loadMethod(FunctionMethodDescriptor descriptor, Map(descriptor.getName(), functionDefinition)); } - private void initializeOneTimeLogics() { + private void initializeOneTimeLogics() throws Exception{ if (!oneTimeLogicInitialized) { synchronized (oneTimeLogicInitializationLock) { if (!oneTimeLogicInitialized) { initializeInvocationChainFactory(); - initializeFunctionInstanceInjector(); + initializeInstanceInjector(); oneTimeLogicInitialized = true; } } @@ -90,28 +93,45 @@ private void initializeInvocationChainFactory() { this.invocationChainFactory = new InvocationChainFactory(middlewares); } - private void initializeFunctionInstanceInjector() { + private void initializeInstanceInjector() throws Exception{ ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader(); try { //ServiceLoader will use thread context classloader to verify loaded class Thread.currentThread().setContextClassLoader(classLoaderProvider.createClassLoader()); - Iterator iterator = ServiceLoader.load(FunctionInstanceInjector.class).iterator(); - if (iterator.hasNext()) { - this.functionInstanceInjector = iterator.next(); - WorkerLogManager.getSystemLogger().info("Load function instance injector: " + this.functionInstanceInjector.getClass().getName()); - if (iterator.hasNext()){ - WorkerLogManager.getSystemLogger().warning("Customer function app has multiple FunctionInstanceInjector implementations."); - throw new RuntimeException("Customer function app has multiple FunctionInstanceInjector implementations"); - } - }else { - this.functionInstanceInjector = newInstanceInjector(); - WorkerLogManager.getSystemLogger().info("Didn't find any function instance injector, creating function class instance every invocation."); - } + loadFunctionInstanceInjector(); + loadGsonInstanceInjector(); } finally { Thread.currentThread().setContextClassLoader(prevContextClassLoader); } } + private void loadFunctionInstanceInjector() { + Iterator iterator = ServiceLoader.load(FunctionInstanceInjector.class).iterator(); + if (iterator.hasNext()) { + this.functionInstanceInjector = iterator.next(); + WorkerLogManager.getSystemLogger().info("Load function instance injector: " + this.functionInstanceInjector.getClass().getName()); + if (iterator.hasNext()){ + WorkerLogManager.getSystemLogger().warning("Customer function app has multiple FunctionInstanceInjector implementations."); + throw new RuntimeException("Customer function app has multiple FunctionInstanceInjector implementations"); + } + }else { + this.functionInstanceInjector = newFunctionInstanceInjector(); + WorkerLogManager.getSystemLogger().info("Didn't find any function instance injector, creating function class instance every invocation."); + } + } + + private void loadGsonInstanceInjector() throws Exception{ + Iterator iterator = ServiceLoader.load(GsonInstanceInjector.class).iterator(); + if (iterator.hasNext()) { + GsonInstanceInjector gsonInstanceInjector = iterator.next(); + Util.setGsonInstance(gsonInstanceInjector.getGsonInstance()); + WorkerLogManager.getSystemLogger().info("Load gson instance injector: " + gsonInstanceInjector.getClass().getName()); + }else { + Util.setGsonInstance(new Gson()); + WorkerLogManager.getSystemLogger().info("Didn't find any gson instance injector, creating function class instance every invocation."); + } + } + private FunctionExecutionMiddleware getFunctionExecutionMiddleWare() { FunctionExecutionMiddleware functionExecutionMiddleware = new FunctionExecutionMiddleware( JavaMethodExecutors.createJavaMethodExecutor(this.classLoaderProvider.createClassLoader())); diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcByteArrayDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcByteArrayDataSourceTest.java index ccfb9317..3ad31c14 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcByteArrayDataSourceTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcByteArrayDataSourceTest.java @@ -4,16 +4,24 @@ import java.lang.invoke.WrongMethodTypeException; import java.util.Optional; +import com.google.gson.Gson; +import com.microsoft.azure.functions.worker.Util; import org.apache.commons.lang3.ArrayUtils; import com.google.protobuf.ByteString; import com.microsoft.azure.functions.worker.binding.BindingData; import com.microsoft.azure.functions.worker.binding.RpcByteArrayDataSource; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class RpcByteArrayDataSourceTest { + @BeforeAll + public static void setGsonInstance() { + Util.setGsonInstance(new Gson()); + } + @Test public void rpcByteArrayDataSource_To_byteArray() { String sourceKey = "testByteArray"; diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcStringDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcStringDataSourceTest.java index 4ce4462b..6b0c6c81 100644 --- a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcStringDataSourceTest.java +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcStringDataSourceTest.java @@ -8,11 +8,14 @@ import java.util.List; import java.util.Optional; +import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; +import com.microsoft.azure.functions.worker.Util; import com.microsoft.azure.functions.worker.binding.BindingData; import com.microsoft.azure.functions.worker.binding.RpcJsonDataSource; import com.microsoft.azure.functions.worker.binding.RpcStringDataSource; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -32,6 +35,11 @@ public void FunctionWithPOJOListInput(ArrayList items) { public void FunctionWithStringListInput(List items) { } + @BeforeAll + public static void setGsonInstance() { + Util.setGsonInstance(new Gson()); + } + @Test public void rpcStringDataSource_To_String() { String sourceKey = "testString";