1313 */
1414package feign .jaxrs ;
1515
16- import feign .Contract ;
17- import feign .MethodMetadata ;
18- import feign .Request ;
19- import javax .ws .rs .*;
20- import java .lang .annotation .Annotation ;
21- import java .lang .reflect .Method ;
22- import java .util .ArrayList ;
23- import java .util .Arrays ;
24- import java .util .Collection ;
25- import java .util .Collections ;
2616import static feign .Util .checkState ;
2717import static feign .Util .emptyToNull ;
2818import static feign .Util .removeValues ;
19+ import java .lang .annotation .Annotation ;
20+ import java .lang .reflect .Method ;
21+ import java .util .Collections ;
22+ import javax .ws .rs .*;
23+ import feign .Contract .DeclarativeContract ;
24+ import feign .MethodMetadata ;
25+ import feign .Request ;
2926
3027/**
3128 * Please refer to the <a href="https://github.com/Netflix/feign/tree/master/feign-jaxrs">Feign
3229 * JAX-RS README</a>.
3330 */
34- public class JAXRSContract extends Contract . BaseContract {
31+ public class JAXRSContract extends DeclarativeContract {
3532
3633 static final String ACCEPT = "Accept" ;
3734 static final String CONTENT_TYPE = "Content-Type" ;
@@ -44,52 +41,49 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
4441 return super .parseAndValidateMetadata (targetType , method );
4542 }
4643
47- @ Override
48- protected void processAnnotationOnClass (MethodMetadata data , Class <?> clz ) {
49- Path path = clz .getAnnotation (Path .class );
50- if (path != null && !path .value ().isEmpty ()) {
51- String pathValue = path .value ();
52- if (!pathValue .startsWith ("/" )) {
53- pathValue = "/" + pathValue ;
54- }
55- if (pathValue .endsWith ("/" )) {
56- // Strip off any trailing slashes, since the template has already had slashes appropriately
57- // added
58- pathValue = pathValue .substring (0 , pathValue .length () - 1 );
44+ public JAXRSContract () {
45+ super .registerClassAnnotation (Path .class , (path , data ) -> {
46+ if (path != null && !path .value ().isEmpty ()) {
47+ String pathValue = path .value ();
48+ if (!pathValue .startsWith ("/" )) {
49+ pathValue = "/" + pathValue ;
50+ }
51+ if (pathValue .endsWith ("/" )) {
52+ // Strip off any trailing slashes, since the template has already had slashes
53+ // appropriately
54+ // added
55+ pathValue = pathValue .substring (0 , pathValue .length () - 1 );
56+ }
57+ // jax-rs allows whitespace around the param name, as well as an optional regex. The
58+ // contract
59+ // should
60+ // strip these out appropriately.
61+ pathValue = pathValue .replaceAll ("\\ {\\ s*(.+?)\\ s*(:.+?)?\\ }" , "\\ {$1\\ }" );
62+ data .template ().uri (pathValue );
5963 }
60- // jax-rs allows whitespace around the param name, as well as an optional regex. The contract
61- // should
62- // strip these out appropriately.
63- pathValue = pathValue .replaceAll ("\\ {\\ s*(.+?)\\ s*(:.+?)?\\ }" , "\\ {$1\\ }" );
64- data .template ().uri (pathValue );
65- }
66- Consumes consumes = clz .getAnnotation (Consumes .class );
67- if (consumes != null ) {
68- handleConsumesAnnotation (data , consumes , clz .getName ());
69- }
70- Produces produces = clz .getAnnotation (Produces .class );
71- if (produces != null ) {
72- handleProducesAnnotation (data , produces , clz .getName ());
73- }
74- }
64+ });
65+ super .registerClassAnnotation (Consumes .class , this ::handleConsumesAnnotation );
66+ super .registerClassAnnotation (Produces .class , this ::handleProducesAnnotation );
7567
76- @ Override
77- protected void processAnnotationOnMethod ( MethodMetadata data ,
78- Annotation methodAnnotation ,
79- Method method ) {
80- Class <? extends Annotation > annotationType = methodAnnotation . annotationType ();
81- HttpMethod http = annotationType . getAnnotation ( HttpMethod . class );
82- if ( http != null ) {
68+ registerMethodAnnotation ( methodAnnotation -> {
69+ Class <? extends Annotation > annotationType = methodAnnotation . annotationType ();
70+ HttpMethod http = annotationType . getAnnotation ( HttpMethod . class );
71+ return http != null ;
72+ }, ( methodAnnotation , data ) -> {
73+ Class <? extends Annotation > annotationType = methodAnnotation . annotationType ( );
74+ HttpMethod http = annotationType . getAnnotation ( HttpMethod . class );
8375 checkState (data .template ().method () == null ,
84- "Method %s contains multiple HTTP methods. Found: %s and %s" , method . getName (),
76+ "Method %s contains multiple HTTP methods. Found: %s and %s" , data . configKey (),
8577 data .template ().method (), http .value ());
8678 data .template ().method (Request .HttpMethod .valueOf (http .value ()));
87- } else if (annotationType == Path .class ) {
88- String pathValue = emptyToNull (Path .class .cast (methodAnnotation ).value ());
79+ });
80+
81+ super .registerMethodAnnotation (Path .class , (path , data ) -> {
82+ final String pathValue = emptyToNull (path .value ());
8983 if (pathValue == null ) {
9084 return ;
9185 }
92- String methodAnnotationValue = Path . class . cast ( methodAnnotation ) .value ();
86+ String methodAnnotationValue = path .value ();
9387 if (!methodAnnotationValue .startsWith ("/" ) && !data .template ().url ().endsWith ("/" )) {
9488 methodAnnotationValue = "/" + methodAnnotationValue ;
9589 }
@@ -99,83 +93,62 @@ protected void processAnnotationOnMethod(MethodMetadata data,
9993 methodAnnotationValue =
10094 methodAnnotationValue .replaceAll ("\\ {\\ s*(.+?)\\ s*(:.+?)?\\ }" , "\\ {$1\\ }" );
10195 data .template ().uri (methodAnnotationValue , true );
102- } else if (annotationType == Produces .class ) {
103- handleProducesAnnotation (data , (Produces ) methodAnnotation , "method " + method .getName ());
104- } else if (annotationType == Consumes .class ) {
105- handleConsumesAnnotation (data , (Consumes ) methodAnnotation , "method " + method .getName ());
106- }
96+ });
97+ super .registerMethodAnnotation (Consumes .class , this ::handleConsumesAnnotation );
98+ super .registerMethodAnnotation (Produces .class , this ::handleProducesAnnotation );
99+
100+ // trying to minimize the diff
101+ registerParamAnnotations ();
107102 }
108103
109- private void handleProducesAnnotation (MethodMetadata data , Produces produces , String name ) {
110- String [] serverProduces =
104+ private void handleProducesAnnotation (Produces produces , MethodMetadata data ) {
105+ final String [] serverProduces =
111106 removeValues (produces .value (), (mediaType ) -> emptyToNull (mediaType ) == null , String .class );
112- checkState (serverProduces .length > 0 , "Produces.value() was empty on %s" , name );
107+ checkState (serverProduces .length > 0 , "Produces.value() was empty on %s" , data . configKey () );
113108 data .template ().header (ACCEPT , Collections .emptyList ()); // remove any previous produces
114109 data .template ().header (ACCEPT , serverProduces );
115110 }
116111
117- private void handleConsumesAnnotation (MethodMetadata data , Consumes consumes , String name ) {
118- String [] serverConsumes =
112+ private void handleConsumesAnnotation (Consumes consumes , MethodMetadata data ) {
113+ final String [] serverConsumes =
119114 removeValues (consumes .value (), (mediaType ) -> emptyToNull (mediaType ) == null , String .class );
120- checkState (serverConsumes .length > 0 , "Consumes.value() was empty on %s" , name );
115+ checkState (serverConsumes .length > 0 , "Consumes.value() was empty on %s" , data . configKey () );
121116 data .template ().header (CONTENT_TYPE , Collections .emptyList ()); // remove any previous consumes
122117 data .template ().header (CONTENT_TYPE , serverConsumes [0 ]);
123118 }
124119
125- /**
126- * Allows derived contracts to specify unsupported jax-rs parameter annotations which should be
127- * ignored. Required for JAX-RS 2 compatibility.
128- */
129- protected boolean isUnsupportedHttpParameterAnnotation (Annotation parameterAnnotation ) {
130- return false ;
131- }
132-
133- @ Override
134- protected boolean processAnnotationsOnParameter (MethodMetadata data ,
135- Annotation [] annotations ,
136- int paramIndex ) {
137- boolean isHttpParam = false ;
138- for (Annotation parameterAnnotation : annotations ) {
139- Class <? extends Annotation > annotationType = parameterAnnotation .annotationType ();
140- // masc20180327. parameter with unsupported jax-rs annotations should not be passed as body
141- // params.
142- // this will prevent interfaces from becoming unusable entirely due to single (unsupported)
143- // endpoints.
144- // https://github.com/OpenFeign/feign/issues/669
145- if (this .isUnsupportedHttpParameterAnnotation (parameterAnnotation )) {
146- isHttpParam = true ;
147- } else if (annotationType == PathParam .class ) {
148- String name = PathParam .class .cast (parameterAnnotation ).value ();
120+ protected void registerParamAnnotations () {
121+ {
122+ registerParameterAnnotation (PathParam .class , (param , data , paramIndex ) -> {
123+ final String name = param .value ();
149124 checkState (emptyToNull (name ) != null , "PathParam.value() was empty on parameter %s" ,
150125 paramIndex );
151126 nameParam (data , name , paramIndex );
152- isHttpParam = true ;
153- } else if ( annotationType == QueryParam .class ) {
154- String name = QueryParam . class . cast ( parameterAnnotation ) .value ();
127+ }) ;
128+ registerParameterAnnotation ( QueryParam .class , ( param , data , paramIndex ) -> {
129+ final String name = param .value ();
155130 checkState (emptyToNull (name ) != null , "QueryParam.value() was empty on parameter %s" ,
156131 paramIndex );
157- String query = addTemplatedParam (name );
132+ final String query = addTemplatedParam (name );
158133 data .template ().query (name , query );
159134 nameParam (data , name , paramIndex );
160- isHttpParam = true ;
161- } else if ( annotationType == HeaderParam .class ) {
162- String name = HeaderParam . class . cast ( parameterAnnotation ) .value ();
135+ }) ;
136+ registerParameterAnnotation ( HeaderParam .class , ( param , data , paramIndex ) -> {
137+ final String name = param .value ();
163138 checkState (emptyToNull (name ) != null , "HeaderParam.value() was empty on parameter %s" ,
164139 paramIndex );
165- String header = addTemplatedParam (name );
140+ final String header = addTemplatedParam (name );
166141 data .template ().header (name , header );
167142 nameParam (data , name , paramIndex );
168- isHttpParam = true ;
169- } else if ( annotationType == FormParam .class ) {
170- String name = FormParam . class . cast ( parameterAnnotation ) .value ();
143+ }) ;
144+ registerParameterAnnotation ( FormParam .class , ( param , data , paramIndex ) -> {
145+ final String name = param .value ();
171146 checkState (emptyToNull (name ) != null , "FormParam.value() was empty on parameter %s" ,
172147 paramIndex );
173148 data .formParams ().add (name );
174149 nameParam (data , name , paramIndex );
175- isHttpParam = true ;
176- }
150+ });
177151 }
178- return isHttpParam ;
179152 }
180153
181154 // Not using override as the super-type's method is deprecated and will be removed.
0 commit comments