2323import com .palantir .logsafe .exceptions .SafeIllegalStateException ;
2424import java .lang .annotation .Annotation ;
2525import java .lang .reflect .Method ;
26+ import java .lang .reflect .Modifier ;
27+ import java .net .URI ;
28+ import java .util .ArrayList ;
2629import java .util .Collection ;
30+ import java .util .LinkedHashMap ;
31+ import java .util .List ;
32+ import java .util .Map ;
2733import javax .annotation .Nullable ;
2834
2935/**
3238 * JAXRSContract.java</a> which is licensed under Apache 2.
3339 * We have modified the implementation to handle both jaxrs and jakarta annotations, easing migrations.
3440 */
35- final class CompatibleJaxRsContract extends Contract . BaseContract {
41+ final class JaxRsContract implements Contract {
3642
3743 @ Override
38- void processAnnotationOnClass (MethodMetadata data , Class <?> clz ) {
44+ public List <MethodMetadata > parseAndValidateMetadata (Class <?> targetType ) {
45+ Util .checkState (
46+ targetType .getTypeParameters ().length == 0 ,
47+ "Parameterized types unsupported: %s" ,
48+ targetType .getSimpleName ());
49+ Util .checkState (
50+ targetType .getInterfaces ().length <= 1 ,
51+ "Only single inheritance supported: %s" ,
52+ targetType .getSimpleName ());
53+ if (targetType .getInterfaces ().length == 1 ) {
54+ Util .checkState (
55+ targetType .getInterfaces ()[0 ].getInterfaces ().length == 0 ,
56+ "Only single-level inheritance supported: %s" ,
57+ targetType .getSimpleName ());
58+ }
59+ Map <String , MethodMetadata > result = new LinkedHashMap <String , MethodMetadata >();
60+ for (Method method : targetType .getMethods ()) {
61+ if (method .getDeclaringClass () == Object .class
62+ || (method .getModifiers () & Modifier .STATIC ) != 0
63+ || method .isDefault ()) {
64+ continue ;
65+ }
66+ MethodMetadata metadata = parseAndValidateMetadata (targetType , method );
67+ Util .checkState (
68+ !result .containsKey (metadata .configKey ()), "Overrides unsupported: %s" , metadata .configKey ());
69+ result .put (metadata .configKey (), metadata );
70+ }
71+ return new ArrayList <MethodMetadata >(result .values ());
72+ }
73+
74+ private MethodMetadata parseAndValidateMetadata (Class <?> targetType , Method method ) {
75+ MethodMetadata data = new MethodMetadata ();
76+ data .returnType (Types .resolve (targetType , targetType , method .getGenericReturnType ()));
77+ data .configKey (Feign .configKey (targetType , method ));
78+
79+ if (targetType .getInterfaces ().length == 1 ) {
80+ processAnnotationOnClass (data , targetType .getInterfaces ()[0 ]);
81+ }
82+ processAnnotationOnClass (data , targetType );
83+
84+ for (Annotation methodAnnotation : method .getAnnotations ()) {
85+ processAnnotationOnMethod (data , methodAnnotation , method );
86+ }
87+ Util .checkState (
88+ data .template ().method () != null ,
89+ "Method %s not annotated with HTTP method type (ex. GET, POST)" ,
90+ method .getName ());
91+ Class <?>[] parameterTypes = method .getParameterTypes ();
92+
93+ Annotation [][] parameterAnnotations = method .getParameterAnnotations ();
94+ int count = parameterAnnotations .length ;
95+ for (int i = 0 ; i < count ; i ++) {
96+ boolean isHttpAnnotation = false ;
97+ if (parameterAnnotations [i ] != null ) {
98+ isHttpAnnotation = processAnnotationsOnParameter (data , parameterAnnotations [i ], i );
99+ }
100+ if (parameterTypes [i ] == URI .class ) {
101+ data .urlIndex (i );
102+ } else if (!isHttpAnnotation ) {
103+ Util .checkState (data .formParams ().isEmpty (), "Body parameters cannot be used with form parameters." );
104+ Util .checkState (data .bodyIndex () == null , "Method has too many Body parameters: %s" , method );
105+ data .bodyIndex (i );
106+ data .bodyType (Types .resolve (targetType , targetType , method .getGenericParameterTypes ()[i ]));
107+ }
108+ }
109+
110+ if (data .headerMapIndex () != null ) {
111+ Util .checkState (
112+ Map .class .isAssignableFrom (parameterTypes [data .headerMapIndex ()]),
113+ "HeaderMap parameter must be a Map: %s" ,
114+ parameterTypes [data .headerMapIndex ()]);
115+ }
116+
117+ if (data .queryMapIndex () != null ) {
118+ Util .checkState (
119+ Map .class .isAssignableFrom (parameterTypes [data .queryMapIndex ()]),
120+ "QueryMap parameter must be a Map: %s" ,
121+ parameterTypes [data .queryMapIndex ()]);
122+ }
123+
124+ return data ;
125+ }
126+
127+ private void processAnnotationOnClass (MethodMetadata data , Class <?> clz ) {
39128 Annotation path = Annotations .PATH .getAnnotation (clz );
40129 if (path != null ) {
41130 String pathValue = Strings .emptyToNull (getAnnotationValue (path ));
@@ -60,8 +149,7 @@ void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
60149 }
61150 }
62151
63- @ Override
64- void processAnnotationOnMethod (MethodMetadata data , Annotation methodAnnotation , Method method ) {
152+ private void processAnnotationOnMethod (MethodMetadata data , Annotation methodAnnotation , Method method ) {
65153 Class <? extends Annotation > annotationType = methodAnnotation .annotationType ();
66154 Annotation http = Annotations .HTTP_METHOD .getAnnotation (annotationType );
67155 if (http != null ) {
@@ -91,26 +179,7 @@ void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation,
91179 }
92180 }
93181
94- private void handleProducesAnnotation (MethodMetadata data , Annotation produces , String name ) {
95- String [] serverProduces = getAnnotationValues (produces );
96- String clientAccepts =
97- serverProduces == null || serverProduces .length == 0 ? null : Strings .emptyToNull (serverProduces [0 ]);
98- Preconditions .checkState (clientAccepts != null , "Produces.value() was empty" , SafeArg .of ("target" , name ));
99- data .template ().header (HttpHeaders .ACCEPT , (String ) null ); // remove any previous produces
100- data .template ().header (HttpHeaders .ACCEPT , clientAccepts );
101- }
102-
103- private void handleConsumesAnnotation (MethodMetadata data , Annotation consumes , String name ) {
104- String [] serverConsumes = getAnnotationValues (consumes );
105- String clientProduces =
106- serverConsumes == null || serverConsumes .length == 0 ? null : Strings .emptyToNull (serverConsumes [0 ]);
107- Preconditions .checkState (clientProduces != null , "Consumes.value() was empty" , SafeArg .of ("target" , name ));
108- data .template ().header (HttpHeaders .CONTENT_TYPE , (String ) null ); // remove any previous consumes
109- data .template ().header (HttpHeaders .CONTENT_TYPE , clientProduces );
110- }
111-
112- @ Override
113- boolean processAnnotationsOnParameter (MethodMetadata data , Annotation [] annotations , int paramIndex ) {
182+ private boolean processAnnotationsOnParameter (MethodMetadata data , Annotation [] annotations , int paramIndex ) {
114183 boolean isHttpParam = false ;
115184 for (Annotation parameterAnnotation : annotations ) {
116185 Class <? extends Annotation > annotationType =
@@ -159,6 +228,40 @@ boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotati
159228 return isHttpParam ;
160229 }
161230
231+ private void handleProducesAnnotation (MethodMetadata data , Annotation produces , String name ) {
232+ String [] serverProduces = getAnnotationValues (produces );
233+ String clientAccepts =
234+ serverProduces == null || serverProduces .length == 0 ? null : Strings .emptyToNull (serverProduces [0 ]);
235+ Preconditions .checkState (clientAccepts != null , "Produces.value() was empty" , SafeArg .of ("target" , name ));
236+ data .template ().header (HttpHeaders .ACCEPT , (String ) null ); // remove any previous produces
237+ data .template ().header (HttpHeaders .ACCEPT , clientAccepts );
238+ }
239+
240+ private void handleConsumesAnnotation (MethodMetadata data , Annotation consumes , String name ) {
241+ String [] serverConsumes = getAnnotationValues (consumes );
242+ String clientProduces =
243+ serverConsumes == null || serverConsumes .length == 0 ? null : Strings .emptyToNull (serverConsumes [0 ]);
244+ Preconditions .checkState (clientProduces != null , "Consumes.value() was empty" , SafeArg .of ("target" , name ));
245+ data .template ().header (HttpHeaders .CONTENT_TYPE , (String ) null ); // remove any previous consumes
246+ data .template ().header (HttpHeaders .CONTENT_TYPE , clientProduces );
247+ }
248+
249+ @ SuppressWarnings ("ParameterAssignment" )
250+ private static Collection <String > addTemplatedParam (Collection <String > possiblyNull , String name ) {
251+ if (possiblyNull == null ) {
252+ possiblyNull = new ArrayList <String >();
253+ }
254+ possiblyNull .add (String .format ("{%s}" , name ));
255+ return possiblyNull ;
256+ }
257+
258+ private static void nameParam (MethodMetadata data , String name , int index ) {
259+ Collection <String > names =
260+ data .indexToName ().containsKey (index ) ? data .indexToName ().get (index ) : new ArrayList <String >();
261+ names .add (name );
262+ data .indexToName ().put (index , names );
263+ }
264+
162265 @ Nullable
163266 private static String getAnnotationValue (Annotation annotation ) {
164267 return (String ) getAnnotationValueInternal (annotation );
0 commit comments