View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *     http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package org.gbif.ws.client;
15  
16  import java.lang.reflect.Method;
17  import java.lang.reflect.Modifier;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.LinkedHashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.lang3.StringUtils;
25  import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
26  import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
27  import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor;
28  import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
29  import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
30  import org.springframework.cloud.openfeign.support.SpringMvcContract;
31  import org.springframework.web.bind.annotation.RequestMapping;
32  
33  import feign.MethodMetadata;
34  import feign.Util;
35  
36  import static feign.Util.checkState;
37  import static feign.Util.emptyToNull;
38  import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
39  
40  public class ClientContract extends SpringMvcContract {
41  
42    private ClientContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
43      super(annotatedParameterProcessors);
44    }
45  
46    public static ClientContract withDefaultProcessors() {
47      return new ClientContract(
48          Arrays.asList(
49              new PartialDateParameterProcessor(),
50              new PathVariableParameterProcessor(),
51              new RequestParamParameterProcessor(),
52              new RequestHeaderParameterProcessor(),
53              new QueryMapParameterProcessor()));
54    }
55  
56    public static ClientContract withProcessors(
57        AnnotatedParameterProcessor... annotatedParameterProcessors) {
58      return new ClientContract(Arrays.asList(annotatedParameterProcessors));
59    }
60  
61    @Override
62    public List<MethodMetadata> parseAndValidateMetadata(final Class<?> targetType) {
63      checkState(
64          targetType.getTypeParameters().length == 0,
65          "Parameterized types unsupported: %s",
66          targetType.getSimpleName());
67      final Map<String, MethodMetadata> result = new LinkedHashMap<>();
68  
69      for (final Method method : targetType.getMethods()) {
70        if (method.getDeclaringClass() == Object.class
71            || (method.getModifiers() & Modifier.STATIC) != 0
72            || Util.isDefault(method)
73            // skip default methods which related to generic inheritance
74            // also default methods are considered as "unsupported operations"
75            || method.toString().startsWith("public default")
76            // skip not annotated methods (consider as "not implemented")
77            || method.getAnnotations().length == 0) {
78          continue;
79        }
80        final MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
81        checkState(
82            !result.containsKey(metadata.configKey()),
83            "Overrides unsupported: %s",
84            metadata.configKey());
85        result.put(metadata.configKey(), metadata);
86      }
87  
88      return new ArrayList<>(result.values());
89    }
90  
91    @Override
92    protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
93      RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
94      if (classAnnotation != null) {
95        // Prepend path from class annotation if specified
96        if (classAnnotation.value().length > 0) {
97          String pathValue = emptyToNull(classAnnotation.value()[0]);
98          data.template().uri(StringUtils.prependIfMissing(pathValue, "/"));
99        }
100     }
101   }
102 }