1
1
package com .microsoft .azure .functions .worker .binding ;
2
2
3
+ import com .microsoft .azure .functions .*;
4
+ import com .microsoft .azure .functions .internal .spi .middleware .MiddlewareContext ;
5
+ import com .microsoft .azure .functions .rpc .messages .ParameterBinding ;
6
+ import com .microsoft .azure .functions .rpc .messages .TypedData ;
7
+ import com .microsoft .azure .functions .worker .WorkerLogManager ;
8
+ import com .microsoft .azure .functions .worker .broker .MethodBindInfo ;
9
+ import com .microsoft .azure .functions .worker .broker .ParamBindInfo ;
10
+
11
+ import java .lang .annotation .Annotation ;
12
+ import java .lang .reflect .Parameter ;
13
+ import java .lang .reflect .Type ;
14
+ import java .util .HashMap ;
15
+ import java .util .List ;
16
+ import java .util .Map ;
17
+ import java .util .Optional ;
3
18
import java .util .logging .Logger ;
4
19
5
- import com .microsoft .azure .functions .ExecutionContext ;
6
- import com .microsoft .azure .functions .worker .WorkerLogManager ;
7
- import com .microsoft .azure .functions .TraceContext ;
8
- import com .microsoft .azure .functions .RetryContext ;
20
+ public final class ExecutionContextDataSource extends DataSource <ExecutionContext > implements MiddlewareContext {
21
+ private final String invocationId ;
22
+ private final TraceContext traceContext ;
23
+ private final RetryContext retryContext ;
24
+ private final Logger logger ;
25
+ private final String funcname ;
26
+ private final BindingDataStore dataStore ;
27
+ private final MethodBindInfo methodBindInfo ;
28
+ private final Class <?> containingClass ;
29
+
30
+ /*
31
+ Key is the name defined on customer function parameters. For ex:
32
+ @HttpTrigger(
33
+ name = "req",
34
+ methods = {HttpMethod.GET, HttpMethod.POST},
35
+ authLevel = AuthorizationLevel.ANONYMOUS)
36
+ HttpRequestMessage<Optional<String>> request,
37
+ Here name will be the "req".
38
+
39
+ Value is java.lang.reflect.Parameter type
40
+ */
41
+ private final Map <String , Parameter > parameterDefinitions ;
42
+
43
+ // currently the values are only Strings (resolved from grpc String values)
44
+ // but planning to support other types in the future
45
+ private final Map <String , Object > parameterValues ;
46
+
47
+ // these are parameters provided by the middleware, which will override the host provided parameter values
48
+ // currently the values are only Strings, but planning to support other types in the future
49
+ private final Map <String , Object > middlewareParameterValues = new HashMap <>();
50
+ private Object returnValue ;
51
+
52
+ //TODO: refactor class to have subclass dedicate to middleware to make logics clean
53
+ private static final DataOperations <ExecutionContext , Object > EXECONTEXT_DATA_OPERATIONS = new DataOperations <>();
54
+ static {
55
+ EXECONTEXT_DATA_OPERATIONS .addGenericOperation (ExecutionContext .class , DataOperations ::generalAssignment );
56
+ }
9
57
10
- final class ExecutionContextDataSource extends DataSource <ExecutionContext > implements ExecutionContext {
11
- ExecutionContextDataSource (String invocationId , String funcname , TraceContext traceContext , RetryContext retryContext ) {
58
+ public ExecutionContextDataSource (String invocationId , TraceContext traceContext , RetryContext retryContext ,
59
+ String funcname , BindingDataStore dataStore , MethodBindInfo methodBindInfo ,
60
+ Class <?> containingClass , List <ParameterBinding > parameterBindings ){
12
61
super (null , null , EXECONTEXT_DATA_OPERATIONS );
13
62
this .invocationId = invocationId ;
14
63
this .traceContext = traceContext ;
15
64
this .retryContext = retryContext ;
16
65
this .logger = WorkerLogManager .getInvocationLogger (invocationId );
17
66
this .funcname = funcname ;
67
+ this .dataStore = dataStore ;
68
+ this .methodBindInfo = methodBindInfo ;
69
+ this .containingClass = containingClass ;
70
+ this .parameterDefinitions = getParameterDefinitions (methodBindInfo );
71
+ this .parameterValues = resolveParameterValuesForMiddleware (parameterBindings );
18
72
this .setValue (this );
19
73
}
20
74
@@ -32,15 +86,90 @@ final class ExecutionContextDataSource extends DataSource<ExecutionContext> impl
32
86
33
87
@ Override
34
88
public String getFunctionName () { return this .funcname ; }
35
-
36
- private final String invocationId ;
37
- private final TraceContext traceContext ;
38
- private final RetryContext retryContext ;
39
- private final Logger logger ;
40
- private final String funcname ;
41
89
42
- private static final DataOperations <ExecutionContext , Object > EXECONTEXT_DATA_OPERATIONS = new DataOperations <>();
43
- static {
44
- EXECONTEXT_DATA_OPERATIONS .addGenericOperation (ExecutionContext .class , DataOperations ::generalAssignment );
45
- }
90
+ public BindingDataStore getDataStore () {
91
+ return dataStore ;
92
+ }
93
+
94
+ public MethodBindInfo getMethodBindInfo () {
95
+ return methodBindInfo ;
96
+ }
97
+
98
+ public Class <?> getContainingClass () {
99
+ return containingClass ;
100
+ }
101
+
102
+ private static Map <String , Parameter > getParameterDefinitions (MethodBindInfo methodBindInfo ){
103
+ Map <String , Parameter > map = new HashMap <>();
104
+ for (ParamBindInfo paramBindInfo : methodBindInfo .getParams ()) {
105
+ map .put (paramBindInfo .getName (), paramBindInfo .getParameter ());
106
+ }
107
+ return map ;
108
+ }
109
+
110
+
111
+ //TODO: leverage stream to do the check
112
+ @ Override
113
+ public String getParameterName (String annotationSimpleClassName ){
114
+ for (Map .Entry <String , Parameter > entry : this .parameterDefinitions .entrySet ()){
115
+ if (hasAnnotation (entry .getValue (), annotationSimpleClassName )){
116
+ return entry .getKey ();
117
+ }
118
+ }
119
+ return null ;
120
+ }
121
+
122
+ private static boolean hasAnnotation (Parameter parameter , String annotationSimpleClassName ){
123
+ Annotation [] annotations = parameter .getAnnotations ();
124
+ for (Annotation annotation : annotations ) {
125
+ if (annotation .annotationType ().getSimpleName ().equals (annotationSimpleClassName )){
126
+ return true ;
127
+ }
128
+ }
129
+ return false ;
130
+ }
131
+
132
+ // TODO: Refactor the code in V5 to make resolve arguments logics before middleware invocation.
133
+ // for now only supporting String parameter values mapped to String values
134
+ private static Map <String , Object > resolveParameterValuesForMiddleware (List <ParameterBinding > parameterBindings ){
135
+ Map <String , Object > map = new HashMap <>();
136
+ for (ParameterBinding parameterBinding : parameterBindings ) {
137
+ TypedData typedData = parameterBinding .getData ();
138
+ if (typedData .getDataCase () == TypedData .DataCase .STRING ){
139
+ map .put (parameterBinding .getName (), typedData .getString ());
140
+ }
141
+ }
142
+ return map ;
143
+ }
144
+
145
+ @ Override
146
+ public Object getParameterValue (String name ) {
147
+ return this .parameterValues .get (name );
148
+ }
149
+
150
+ @ Override
151
+ public void updateParameterValue (String name , Object value ) {
152
+ this .middlewareParameterValues .put (name , value );
153
+ }
154
+
155
+ @ Override
156
+ public Object getReturnValue () {
157
+ return this .returnValue ;
158
+ }
159
+
160
+ @ Override
161
+ public void updateReturnValue (Object returnValue ) {
162
+ this .returnValue = returnValue ;
163
+ // set the return value that will be sent back to host
164
+ this .dataStore .setDataTargetValue (BindingDataStore .RETURN_NAME , this .returnValue );
165
+ }
166
+
167
+ public Optional <BindingData > getBindingData (String paramName , Type paramType ) {
168
+ Object inputValue = this .middlewareParameterValues .get (paramName );
169
+ if (inputValue != null ) {
170
+ return Optional .of (new BindingData (inputValue ));
171
+ }else {
172
+ return dataStore .getDataByName (paramName , paramType );
173
+ }
174
+ }
46
175
}
0 commit comments