Skip to content

potential support for customize gson #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<azure.functions.java.core.library.version>1.2.0</azure.functions.java.core.library.version>
<azure.functions.java.spi>1.0.0</azure.functions.java.spi>
<azure.functions.java.spi>1.1.0</azure.functions.java.spi>
<azure.functions.java.library.version>2.2.0</azure.functions.java.library.version>
<jupiter.version>5.9.1</jupiter.version>
<mockito-core.version>4.11.0</mockito-core.version>
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/microsoft/azure/functions/worker/Util.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, Object> JSON_DATA_OPERATIONS = new DataOperations<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> T getInstance(Class<T> functionClass) throws Exception {
Expand All @@ -53,20 +56,20 @@ public JavaFunctionBroker(ClassLoaderProvider classLoaderProvider) {
}

public void loadMethod(FunctionMethodDescriptor descriptor, Map<String, BindingInfo> bindings)
throws ClassNotFoundException, NoSuchMethodException, IOException {
throws Exception {
descriptor.validate();
addSearchPathsToClassLoader(descriptor);
initializeOneTimeLogics();
FunctionDefinition functionDefinition = new FunctionDefinition(descriptor, bindings, classLoaderProvider);
this.methods.put(descriptor.getId(), new ImmutablePair<>(descriptor.getName(), functionDefinition));
}

private void initializeOneTimeLogics() {
private void initializeOneTimeLogics() throws Exception{
if (!oneTimeLogicInitialized) {
synchronized (oneTimeLogicInitializationLock) {
if (!oneTimeLogicInitialized) {
initializeInvocationChainFactory();
initializeFunctionInstanceInjector();
initializeInstanceInjector();
oneTimeLogicInitialized = true;
}
}
Expand All @@ -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<FunctionInstanceInjector> 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<FunctionInstanceInjector> 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<GsonInstanceInjector> 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()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand All @@ -32,6 +35,11 @@ public void FunctionWithPOJOListInput(ArrayList<TestPOJO> items) {
public void FunctionWithStringListInput(List<String> items) {
}

@BeforeAll
public static void setGsonInstance() {
Util.setGsonInstance(new Gson());
}

@Test
public void rpcStringDataSource_To_String() {
String sourceKey = "testString";
Expand Down