001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.ws.client; 015 016import java.lang.reflect.Method; 017import java.lang.reflect.Modifier; 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.lang3.StringUtils; 025import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; 026import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor; 027import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor; 028import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor; 029import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor; 030import org.springframework.cloud.openfeign.support.SpringMvcContract; 031import org.springframework.web.bind.annotation.RequestMapping; 032 033import feign.MethodMetadata; 034import feign.Util; 035 036import static feign.Util.checkState; 037import static feign.Util.emptyToNull; 038import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation; 039 040public class ClientContract extends SpringMvcContract { 041 042 private ClientContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) { 043 super(annotatedParameterProcessors); 044 } 045 046 public static ClientContract withDefaultProcessors() { 047 return new ClientContract( 048 Arrays.asList( 049 new PartialDateParameterProcessor(), 050 new PathVariableParameterProcessor(), 051 new RequestParamParameterProcessor(), 052 new RequestHeaderParameterProcessor(), 053 new QueryMapParameterProcessor())); 054 } 055 056 public static ClientContract withProcessors( 057 AnnotatedParameterProcessor... annotatedParameterProcessors) { 058 return new ClientContract(Arrays.asList(annotatedParameterProcessors)); 059 } 060 061 @Override 062 public List<MethodMetadata> parseAndValidateMetadata(final Class<?> targetType) { 063 checkState( 064 targetType.getTypeParameters().length == 0, 065 "Parameterized types unsupported: %s", 066 targetType.getSimpleName()); 067 final Map<String, MethodMetadata> result = new LinkedHashMap<>(); 068 069 for (final Method method : targetType.getMethods()) { 070 if (method.getDeclaringClass() == Object.class 071 || (method.getModifiers() & Modifier.STATIC) != 0 072 || Util.isDefault(method) 073 // skip default methods which related to generic inheritance 074 // also default methods are considered as "unsupported operations" 075 || method.toString().startsWith("public default") 076 // skip not annotated methods (consider as "not implemented") 077 || method.getAnnotations().length == 0) { 078 continue; 079 } 080 final MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method); 081 checkState( 082 !result.containsKey(metadata.configKey()), 083 "Overrides unsupported: %s", 084 metadata.configKey()); 085 result.put(metadata.configKey(), metadata); 086 } 087 088 return new ArrayList<>(result.values()); 089 } 090 091 @Override 092 protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) { 093 RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class); 094 if (classAnnotation != null) { 095 // Prepend path from class annotation if specified 096 if (classAnnotation.value().length > 0) { 097 String pathValue = emptyToNull(classAnnotation.value()[0]); 098 data.template().uri(StringUtils.prependIfMissing(pathValue, "/")); 099 } 100 } 101 } 102}