33
44package com .azure .core .serializer .json .jackson .implementation ;
55
6+ import com .azure .core .implementation .ReflectiveInvoker ;
67import com .azure .core .implementation .ReflectionUtils ;
78import com .azure .core .util .logging .ClientLogger ;
9+ import com .azure .core .util .logging .LogLevel ;
810
9- import java .lang .invoke .MethodHandle ;
10- import java .lang .invoke .MethodHandles ;
11- import java .lang .invoke .MethodType ;
1211import java .lang .reflect .Field ;
13- import java .lang .reflect .Method ;
1412import java .security .PrivilegedAction ;
1513import java .util .HashMap ;
1614import java .util .Locale ;
2220 */
2321final class HeaderCollectionHandler {
2422 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 <>();
2624
2725 // 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 ( );
2927
3028 private final String prefix ;
3129 private final int prefixLength ;
@@ -86,24 +84,23 @@ private boolean usePublicSetter(Object deserializedHeaders, ClientLogger logger)
8684 final String clazzSimpleName = clazz .getSimpleName ();
8785 final String fieldName = declaringField .getName ();
8886
89- MethodHandle setterHandler = getFromCache (declaringField , clazz , clazzSimpleName , fieldName , logger );
87+ ReflectiveInvoker
88+ setterReflectiveInvoker = getFromCache (declaringField , clazz , clazzSimpleName , fieldName , logger );
9089
91- if (setterHandler == NO_SETTER_HANDLE ) {
90+ if (setterReflectiveInvoker == NO_SETTER_REFLECTIVE_INVOKER ) {
9291 return false ;
9392 }
9493
9594 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." );
9898
9999 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 );
107104 return false ;
108105 }
109106 }
@@ -112,58 +109,27 @@ private static String getPotentialSetterName(String fieldName) {
112109 return "set" + fieldName .substring (0 , 1 ).toUpperCase (Locale .ROOT ) + fieldName .substring (1 );
113110 }
114111
115- private static MethodHandle getFromCache (Field key , Class <?> clazz , String clazzSimpleName ,
112+ private static ReflectiveInvoker getFromCache (Field key , Class <?> clazz , String clazzSimpleName ,
116113 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 ();
119116 }
120117
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 -> {
139119 String setterName = getPotentialSetterName (fieldName );
140120
141121 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 ));
144124
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 + "." );
146127
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 );
167133 }
168134
169135 // 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
174140 // Now the implementation returns a dummy constant when there is no setter for the field. This now results
175141 // in this case properly inserting into the cache and only running when a new type is seen or the cache is
176142 // cleared due to reaching capacity.
177- return NO_SETTER_HANDLE ;
143+ return NO_SETTER_REFLECTIVE_INVOKER ;
178144 });
179145 }
180146}
0 commit comments