3
3
4
4
package com .azure .core .serializer .json .jackson .implementation ;
5
5
6
+ import com .azure .core .implementation .ReflectiveInvoker ;
6
7
import com .azure .core .implementation .ReflectionUtils ;
7
8
import com .azure .core .util .logging .ClientLogger ;
9
+ import com .azure .core .util .logging .LogLevel ;
8
10
9
- import java .lang .invoke .MethodHandle ;
10
- import java .lang .invoke .MethodHandles ;
11
- import java .lang .invoke .MethodType ;
12
11
import java .lang .reflect .Field ;
13
- import java .lang .reflect .Method ;
14
12
import java .security .PrivilegedAction ;
15
13
import java .util .HashMap ;
16
14
import java .util .Locale ;
22
20
*/
23
21
final class HeaderCollectionHandler {
24
22
private static final int CACHE_SIZE_LIMIT = 10000 ;
25
- private static final Map <Field , MethodHandle > FIELD_TO_SETTER_CACHE = new ConcurrentHashMap <>();
23
+ private static final Map <Field , ReflectiveInvoker > FIELD_TO_SETTER_INVOKER_CACHE = new ConcurrentHashMap <>();
26
24
27
25
// Dummy constant that indicates no setter was found for the Field.
28
- private static final MethodHandle NO_SETTER_HANDLE = MethodHandles . identity ( HeaderCollectionHandler . class );
26
+ private static final ReflectiveInvoker NO_SETTER_REFLECTIVE_INVOKER = ReflectionUtils . createNoOpInvoker ( );
29
27
30
28
private final String prefix ;
31
29
private final int prefixLength ;
@@ -86,24 +84,23 @@ private boolean usePublicSetter(Object deserializedHeaders, ClientLogger logger)
86
84
final String clazzSimpleName = clazz .getSimpleName ();
87
85
final String fieldName = declaringField .getName ();
88
86
89
- MethodHandle setterHandler = getFromCache (declaringField , clazz , clazzSimpleName , fieldName , logger );
87
+ ReflectiveInvoker
88
+ setterReflectiveInvoker = getFromCache (declaringField , clazz , clazzSimpleName , fieldName , logger );
90
89
91
- if (setterHandler == NO_SETTER_HANDLE ) {
90
+ if (setterReflectiveInvoker == NO_SETTER_REFLECTIVE_INVOKER ) {
92
91
return false ;
93
92
}
94
93
95
94
try {
96
- setterHandler .invokeWithArguments (deserializedHeaders , values );
97
- logger .verbose ("Set header collection {} on class {} using MethodHandle." , fieldName , clazzSimpleName );
95
+ setterReflectiveInvoker .invokeWithArguments (deserializedHeaders , values );
96
+ logger .log (LogLevel .VERBOSE , () ->
97
+ "Set header collection " + fieldName + " on class " + clazzSimpleName + " using reflection." );
98
98
99
99
return true ;
100
- } catch (Throwable ex ) {
101
- if (ex instanceof Error ) {
102
- throw (Error ) ex ;
103
- }
104
-
105
- logger .verbose ("Failed to set header {} collection on class {} using MethodHandle." , fieldName ,
106
- clazzSimpleName , ex );
100
+ } catch (Exception ex ) {
101
+ logger .log (LogLevel .VERBOSE , () ->
102
+ "Failed to set header " + fieldName + " collection on class " + clazzSimpleName + " using reflection." ,
103
+ ex );
107
104
return false ;
108
105
}
109
106
}
@@ -112,58 +109,27 @@ private static String getPotentialSetterName(String fieldName) {
112
109
return "set" + fieldName .substring (0 , 1 ).toUpperCase (Locale .ROOT ) + fieldName .substring (1 );
113
110
}
114
111
115
- private static MethodHandle getFromCache (Field key , Class <?> clazz , String clazzSimpleName ,
112
+ private static ReflectiveInvoker getFromCache (Field key , Class <?> clazz , String clazzSimpleName ,
116
113
String fieldName , ClientLogger logger ) {
117
- if (FIELD_TO_SETTER_CACHE .size () >= CACHE_SIZE_LIMIT ) {
118
- FIELD_TO_SETTER_CACHE .clear ();
114
+ if (FIELD_TO_SETTER_INVOKER_CACHE .size () >= CACHE_SIZE_LIMIT ) {
115
+ FIELD_TO_SETTER_INVOKER_CACHE .clear ();
119
116
}
120
117
121
- return FIELD_TO_SETTER_CACHE .computeIfAbsent (key , field -> {
122
- MethodHandles .Lookup lookupToUse ;
123
- try {
124
- lookupToUse = ReflectionUtils .getLookupToUse (clazz );
125
- } catch (Exception ex ) {
126
- logger .verbose ("Failed to retrieve MethodHandles.Lookup for field {}. Will attempt to make field accessible." , field , ex );
127
-
128
- // In a previous implementation compute returned null here in an attempt to indicate that there is no
129
- // setter for the field. Unfortunately, null isn't a valid indicator to computeIfAbsent that a
130
- // computation has been performed and this cache would never effectively be a cache as compute would
131
- // always be performed when there was no setter for the field.
132
- //
133
- // Now the implementation returns a dummy constant when there is no setter for the field. This now
134
- // results in this case properly inserting into the cache and only running when a new type is seen or
135
- // the cache is cleared due to reaching capacity.
136
- return NO_SETTER_HANDLE ;
137
- }
138
-
118
+ return FIELD_TO_SETTER_INVOKER_CACHE .computeIfAbsent (key , field -> {
139
119
String setterName = getPotentialSetterName (fieldName );
140
120
141
121
try {
142
- MethodHandle handle = lookupToUse . findVirtual (clazz , setterName ,
143
- MethodType . methodType ( clazz , Map .class ));
122
+ ReflectiveInvoker reflectiveInvoker = ReflectionUtils . getMethodInvoker (clazz , clazz . getDeclaredMethod ( setterName ,
123
+ Map .class ));
144
124
145
- logger .verbose ("Using MethodHandle for setter {} on class {}." , setterName , clazzSimpleName );
125
+ logger .log (LogLevel .VERBOSE , () ->
126
+ "Using invoker for setter " + setterName + " on class " + clazzSimpleName + "." );
146
127
147
- return handle ;
148
- } catch (ReflectiveOperationException ex ) {
149
- logger .verbose ("Failed to retrieve MethodHandle for setter {} on class {}. "
150
- + "Will attempt to make field accessible. "
151
- + "Please consider adding public setter." , setterName ,
152
- clazzSimpleName , ex );
153
- }
154
-
155
- try {
156
- Method setterMethod = clazz .getDeclaredMethod (setterName , Map .class );
157
- MethodHandle handle = lookupToUse .unreflect (setterMethod );
158
-
159
- logger .verbose ("Using unreflected MethodHandle for setter {} on class {}." , setterName ,
160
- clazzSimpleName );
161
-
162
- return handle ;
163
- } catch (ReflectiveOperationException ex ) {
164
- logger .verbose ("Failed to unreflect MethodHandle for setter {} on class {}."
165
- + "Will attempt to make field accessible. "
166
- + "Please consider adding public setter." , setterName , clazzSimpleName , ex );
128
+ return reflectiveInvoker ;
129
+ } catch (Exception ex ) {
130
+ logger .log (LogLevel .VERBOSE , () ->
131
+ "Failed to retrieve invoker for setter " + setterName + " on class " + clazzSimpleName
132
+ + ". Will attempt to make field accessible. Please consider adding public setter." , ex );
167
133
}
168
134
169
135
// In a previous implementation compute returned null here in an attempt to indicate that there is no setter
@@ -174,7 +140,7 @@ private static MethodHandle getFromCache(Field key, Class<?> clazz, String clazz
174
140
// Now the implementation returns a dummy constant when there is no setter for the field. This now results
175
141
// in this case properly inserting into the cache and only running when a new type is seen or the cache is
176
142
// cleared due to reaching capacity.
177
- return NO_SETTER_HANDLE ;
143
+ return NO_SETTER_REFLECTIVE_INVOKER ;
178
144
});
179
145
}
180
146
}
0 commit comments