Skip to content

Commit bd9102e

Browse files
authored
add function instance injector supports for dependency injection (#667)
* add di injector supports * refactor code * udpate log formatting * update to non static - change back to log.info
1 parent ee19add commit bd9102e

File tree

4 files changed

+65
-20
lines changed

4 files changed

+65
-20
lines changed

src/main/java/com/microsoft/azure/functions/worker/binding/ExecutionContextDataSource.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext;
55
import com.microsoft.azure.functions.rpc.messages.ParameterBinding;
66
import com.microsoft.azure.functions.rpc.messages.TypedData;
7+
import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector;
78
import com.microsoft.azure.functions.worker.WorkerLogManager;
89
import com.microsoft.azure.functions.worker.broker.MethodBindInfo;
910
import com.microsoft.azure.functions.worker.broker.ParamBindInfo;
@@ -49,6 +50,8 @@ public final class ExecutionContextDataSource extends DataSource<ExecutionContex
4950
private final Map<String, Object> middlewareParameterValues = new HashMap<>();
5051
private Object returnValue;
5152

53+
private final FunctionInstanceInjector functionInstanceInjector;
54+
5255
//TODO: refactor class to have subclass dedicate to middleware to make logics clean
5356
private static final DataOperations<ExecutionContext, Object> EXECONTEXT_DATA_OPERATIONS = new DataOperations<>();
5457
static {
@@ -57,7 +60,7 @@ public final class ExecutionContextDataSource extends DataSource<ExecutionContex
5760

5861
public ExecutionContextDataSource(String invocationId, TraceContext traceContext, RetryContext retryContext,
5962
String funcname, BindingDataStore dataStore, MethodBindInfo methodBindInfo,
60-
Class<?> containingClass, List<ParameterBinding> parameterBindings){
63+
Class<?> containingClass, List<ParameterBinding> parameterBindings, FunctionInstanceInjector functionInstanceInjector){
6164
super(null, null, EXECONTEXT_DATA_OPERATIONS);
6265
this.invocationId = invocationId;
6366
this.traceContext = traceContext;
@@ -69,6 +72,7 @@ public ExecutionContextDataSource(String invocationId, TraceContext traceContext
6972
this.containingClass = containingClass;
7073
this.parameterDefinitions = getParameterDefinitions(methodBindInfo);
7174
this.parameterValues = resolveParameterValuesForMiddleware(parameterBindings);
75+
this.functionInstanceInjector = functionInstanceInjector;
7276
this.setValue(this);
7377
}
7478

@@ -95,8 +99,8 @@ public MethodBindInfo getMethodBindInfo() {
9599
return methodBindInfo;
96100
}
97101

98-
public Class<?> getContainingClass() {
99-
return containingClass;
102+
public Object getFunctionInstance() throws Exception {
103+
return this.functionInstanceInjector.getInstance(containingClass);
100104
}
101105

102106
private static Map<String, Parameter> getParameterDefinitions(MethodBindInfo methodBindInfo){

src/main/java/com/microsoft/azure/functions/worker/broker/EnhancedJavaMethodExecutorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public void execute(ExecutionContextDataSource executionContextDataSource) throw
1919
Thread.currentThread().setContextClassLoader(this.classLoader);
2020
Object retValue = ParameterResolver.resolveArguments(executionContextDataSource)
2121
.orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input"))
22-
.invoke(() -> executionContextDataSource.getContainingClass().newInstance());
22+
.invoke(executionContextDataSource::getFunctionInstance);
2323
executionContextDataSource.updateReturnValue(retValue);
2424
} finally {
2525
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());

src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import java.util.*;
88
import java.util.concurrent.ConcurrentHashMap;
99
import java.util.concurrent.atomic.AtomicBoolean;
10+
import java.util.logging.Level;
1011

1112
import com.microsoft.azure.functions.internal.spi.middleware.Middleware;
1213
import com.microsoft.azure.functions.rpc.messages.*;
14+
import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector;
1315
import com.microsoft.azure.functions.worker.Constants;
1416
import com.microsoft.azure.functions.worker.WorkerLogManager;
1517
import com.microsoft.azure.functions.worker.binding.BindingDataStore;
@@ -33,8 +35,19 @@ public class JavaFunctionBroker {
3335
private final Map<String, ImmutablePair<String, FunctionDefinition>> methods;
3436
private final ClassLoaderProvider classLoaderProvider;
3537
private String workerDirectory;
36-
private final AtomicBoolean invocationChainFactoryInitialized = new AtomicBoolean(false);
38+
private final AtomicBoolean oneTimeLogicInitialized = new AtomicBoolean(false);
3739
private volatile InvocationChainFactory invocationChainFactory;
40+
private volatile FunctionInstanceInjector functionInstanceInjector;
41+
42+
private FunctionInstanceInjector newInstanceInjector() {
43+
return new FunctionInstanceInjector() {
44+
@Override
45+
public <T> T getInstance(Class<T> functionClass) throws Exception {
46+
return functionClass.newInstance();
47+
}
48+
};
49+
}
50+
3851
public JavaFunctionBroker(ClassLoaderProvider classLoaderProvider) {
3952
this.methods = new ConcurrentHashMap<>();
4053
this.classLoaderProvider = classLoaderProvider;
@@ -44,26 +57,54 @@ public void loadMethod(FunctionMethodDescriptor descriptor, Map<String, BindingI
4457
throws ClassNotFoundException, NoSuchMethodException, IOException {
4558
descriptor.validate();
4659
addSearchPathsToClassLoader(descriptor);
47-
initializeInvocationChainFactory();
60+
initializeOneTimeLogics();
4861
FunctionDefinition functionDefinition = new FunctionDefinition(descriptor, bindings, classLoaderProvider);
4962
this.methods.put(descriptor.getId(), new ImmutablePair<>(descriptor.getName(), functionDefinition));
5063
}
5164

65+
private void initializeOneTimeLogics() {
66+
if (!oneTimeLogicInitialized.getAndSet(true)) {
67+
initializeInvocationChainFactory();
68+
initializeFunctionInstanceInjector();
69+
}
70+
}
71+
5272
private void initializeInvocationChainFactory() {
53-
if (!invocationChainFactoryInitialized.getAndSet(true)) {
54-
ArrayList<Middleware> middlewares = new ArrayList<>();
55-
try {
56-
//ServiceLoader will use thread context classloader to verify loaded class
57-
Thread.currentThread().setContextClassLoader(classLoaderProvider.createClassLoader());
58-
for (Middleware middleware : ServiceLoader.load(Middleware.class)) {
59-
middlewares.add(middleware);
60-
WorkerLogManager.getSystemLogger().info("Load middleware " + middleware.getClass().getSimpleName());
73+
ArrayList<Middleware> middlewares = new ArrayList<>();
74+
ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader();
75+
try {
76+
//ServiceLoader will use thread context classloader to verify loaded class
77+
Thread.currentThread().setContextClassLoader(classLoaderProvider.createClassLoader());
78+
for (Middleware middleware : ServiceLoader.load(Middleware.class)) {
79+
middlewares.add(middleware);
80+
WorkerLogManager.getSystemLogger().info("Load middleware " + middleware.getClass().getSimpleName());
81+
}
82+
} finally {
83+
Thread.currentThread().setContextClassLoader(prevContextClassLoader);
84+
}
85+
middlewares.add(getFunctionExecutionMiddleWare());
86+
this.invocationChainFactory = new InvocationChainFactory(middlewares);
87+
}
88+
89+
private void initializeFunctionInstanceInjector() {
90+
ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader();
91+
try {
92+
//ServiceLoader will use thread context classloader to verify loaded class
93+
Thread.currentThread().setContextClassLoader(classLoaderProvider.createClassLoader());
94+
Iterator<FunctionInstanceInjector> iterator = ServiceLoader.load(FunctionInstanceInjector.class).iterator();
95+
if (iterator.hasNext()) {
96+
this.functionInstanceInjector = iterator.next();
97+
WorkerLogManager.getSystemLogger().info("Load function instance injector: " + this.functionInstanceInjector.getClass().getName());
98+
if (iterator.hasNext()){
99+
WorkerLogManager.getSystemLogger().warning("Customer function app has multiple FunctionInstanceInjector implementations.");
100+
throw new RuntimeException("Customer function app has multiple FunctionInstanceInjector implementations");
61101
}
62-
} finally {
63-
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
102+
}else {
103+
this.functionInstanceInjector = newInstanceInjector();
104+
WorkerLogManager.getSystemLogger().info("Didn't find any function instance injector, creating function class instance every invocation.");
64105
}
65-
middlewares.add(getFunctionExecutionMiddleWare());
66-
this.invocationChainFactory = new InvocationChainFactory(middlewares);
106+
} finally {
107+
Thread.currentThread().setContextClassLoader(prevContextClassLoader);
67108
}
68109
}
69110

@@ -99,7 +140,7 @@ private ExecutionContextDataSource buildExecutionContext(String id, InvocationR
99140
request.getRetryContext().getMaxRetryCount(), request.getRetryContext().getException());
100141
ExecutionContextDataSource executionContextDataSource = new ExecutionContextDataSource(request.getInvocationId(),
101142
traceContext, retryContext, methodEntry.left, dataStore, functionDefinition.getCandidate(),
102-
functionDefinition.getContainingClass(), request.getInputDataList());
143+
functionDefinition.getContainingClass(), request.getInputDataList(), this.functionInstanceInjector);
103144
dataStore.addExecutionContextSource(executionContextDataSource);
104145
return executionContextDataSource;
105146
}

src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private JavaMethodExecutorImpl () {}
1919
public void execute(ExecutionContextDataSource executionContextDataSource) throws Exception {
2020
Object retValue = ParameterResolver.resolveArguments(executionContextDataSource)
2121
.orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input"))
22-
.invoke(() -> executionContextDataSource.getContainingClass().newInstance());
22+
.invoke(executionContextDataSource::getFunctionInstance);
2323
executionContextDataSource.updateReturnValue(retValue);
2424
}
2525
}

0 commit comments

Comments
 (0)