/* * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package feign; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.net.URI; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.HeaderParam; import javax.ws.rs.HttpMethod; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import static com.google.common.base.Preconditions.checkState; import static com.google.common.net.HttpHeaders.ACCEPT; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; /** * Defines what annotations and values are valid on interfaces. */ public final class Contract { public static ImmutableSet parseAndValidatateMetadata(Class declaring) { ImmutableSet.Builder builder = ImmutableSet.builder(); for (Method method : declaring.getDeclaredMethods()) { if (method.getDeclaringClass() == Object.class) continue; builder.add(parseAndValidatateMetadata(method)); } return builder.build(); } public static MethodMetadata parseAndValidatateMetadata(Method method) { MethodMetadata data = new MethodMetadata(); data.returnType(TypeToken.of(method.getGenericReturnType())); data.configKey(Feign.configKey(method)); for (Annotation methodAnnotation : method.getAnnotations()) { Class annotationType = methodAnnotation.annotationType(); HttpMethod http = annotationType.getAnnotation(HttpMethod.class); if (http != null) { checkState(data.template().method() == null, "Method %s contains multiple HTTP methods. Found: %s and %s", method.getName(), data.template() .method(), http.value()); data.template().method(http.value()); } else if (annotationType == RequestTemplate.Body.class) { String body = RequestTemplate.Body.class.cast(methodAnnotation).value(); if (body.indexOf('{') == -1) { data.template().body(body); } else { data.template().bodyTemplate(body); } } else if (annotationType == Path.class) { data.template().append(Path.class.cast(methodAnnotation).value()); } else if (annotationType == Produces.class) { data.template().header(CONTENT_TYPE, Joiner.on(',').join(((Produces) methodAnnotation).value())); } else if (annotationType == Consumes.class) { data.template().header(ACCEPT, Joiner.on(',').join(((Consumes) methodAnnotation).value())); } } checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", method.getName()); Class[] parameterTypes = method.getParameterTypes(); Annotation[][] parameterAnnotationArrays = method.getParameterAnnotations(); int count = parameterAnnotationArrays.length; for (int i = 0; i < count; i++) { boolean hasHttpAnnotation = false; Class parameterType = parameterTypes[i]; Annotation[] parameterAnnotations = parameterAnnotationArrays[i]; if (parameterAnnotations != null) { for (Annotation parameterAnnotation : parameterAnnotations) { Class annotationType = parameterAnnotation.annotationType(); if (annotationType == PathParam.class) { data.indexToName().put(i, PathParam.class.cast(parameterAnnotation).value()); hasHttpAnnotation = true; } else if (annotationType == QueryParam.class) { String name = QueryParam.class.cast(parameterAnnotation).value(); data.template().query( name, ImmutableList.builder().addAll(data.template().queries().get(name)) .add(String.format("{%s}", name)).build()); data.indexToName().put(i, name); hasHttpAnnotation = true; } else if (annotationType == HeaderParam.class) { String name = HeaderParam.class.cast(parameterAnnotation).value(); data.template().header( name, ImmutableList.builder().addAll(data.template().headers().get(name)) .add(String.format("{%s}", name)).build()); data.indexToName().put(i, name); hasHttpAnnotation = true; } else if (annotationType == FormParam.class) { String form = FormParam.class.cast(parameterAnnotation).value(); data.formParams().add(form); data.indexToName().put(i, form); hasHttpAnnotation = true; } } } if (parameterType == URI.class) { data.urlIndex(i); } else if (!hasHttpAnnotation) { checkState(data.formParams().isEmpty(), "Body parameters cannot be used with @FormParam parameters."); checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); data.bodyIndex(i); } } return data; } }