net.paslavsky.springrest.SpringAnnotationPreprocessor.java Source code

Java tutorial

Introduction

Here is the source code for net.paslavsky.springrest.SpringAnnotationPreprocessor.java

Source

/*
 * Copyright (c) 2014 Andrey Paslavsky.
 *
 * 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 net.paslavsky.springrest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * TODO Class description
 *
 * @author Andrey Paslavsky
 * @version 1.0
 */
public class SpringAnnotationPreprocessor implements AnnotationPreprocessor {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public RestMethodMetadata parse(Class<?> clientClass, Method method) {
        RestMethodMetadata metadata = new RestMethodMetadata();

        RequestMapping classMapping = clientClass.getAnnotation(RequestMapping.class);
        RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);

        metadata.setCommonPath(getPath(classMapping));
        metadata.setAdditionalPath(getPath(methodMapping));
        metadata.setHttpMethod(getHttpMethod(classMapping, methodMapping));
        metadata.setResponseClass(getResponseType(method));
        metadata.setMethodReturnType(method.getReturnType());
        metadata.setRequestHeaderParameters(getParametersWithAnnotation(method, RequestHeader.class));
        metadata.setUriVarParameters(getParametersWithAnnotation(method, PathVariable.class));
        metadata.setQueryParameters(getParametersWithAnnotation(method, RequestParam.class));
        metadata.setRequestParameter(getRequestParameter(method));

        return metadata;
    }

    private Integer getRequestParameter(Method method) {
        Integer requestParameter = null;
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0, parameterAnnotationsLength = parameterAnnotations.length; i < parameterAnnotationsLength; i++) {
            Annotation[] annotations = parameterAnnotations[i];
            if (isAnnotationPresent(annotations, RequestBody.class)) {
                requestParameter = i;
            }
        }
        return requestParameter;
    }

    private String getPath(RequestMapping classMapping) {
        if (classMapping != null && isNotEmpty(classMapping.value())) {
            return classMapping.value()[0];
        }
        return null;
    }

    private static <T> boolean isNotEmpty(T[] array) {
        return array != null && array.length > 0;
    }

    private HttpMethod getHttpMethod(RequestMapping classMapping, RequestMapping methodMapping) {
        HttpMethod httpMethod;
        if (methodMapping != null && isNotEmpty(methodMapping.method())) {
            httpMethod = getHttpMethod(methodMapping);
        } else if (classMapping != null && isNotEmpty(classMapping.method())) {
            httpMethod = getHttpMethod(classMapping);
        } else {
            // TODO add warning to the logs
            throw new SpringRestClientConfigurationException("Can't identify HTTP method!");
        }
        return httpMethod;
    }

    private HttpMethod getHttpMethod(RequestMapping mapping) {
        return HttpMethod.valueOf(mapping.method()[0].name());
    }

    private Class<?> getResponseType(Method method) {
        Class<?> type = getResponseType0(method);
        if (type == Object.class) {
            logger.warn("REST client can't identify response type of the method {}.", method);
            logger.warn("Response will be returned as byte array", new RuntimeException());
            return byte[].class;
        }
        return type;
    }

    private Class<?> getResponseType0(Method method) {
        if (method.isAnnotationPresent(ResponseBody.class)) {
            return method.getReturnType();
        } else {
            if (method.getReturnType() == ResponseEntity.class) {
                Type methodGenericReturnType = method.getGenericReturnType();
                if (methodGenericReturnType instanceof ParameterizedTypeImpl) {
                    ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) methodGenericReturnType;
                    Type type = parameterizedType.getActualTypeArguments()[0];
                    return typeToClass(type);
                } else {
                    // TODO Message to the log: recommendation to use ResponseEntity with generic parameter
                    return byte[].class;
                }
            }
            return Void.class;
        }
    }

    private Class<?> typeToClass(Type type) {
        if (type instanceof ParameterizedTypeImpl) {
            return ((ParameterizedTypeImpl) type).getRawType();
        } else {
            return (Class) type;
        }
    }

    private Map<String, Integer> getParametersWithAnnotation(Method method,
            Class<? extends Annotation> annotationClass) {
        Map<String, Integer> parameters = new HashMap<String, Integer>();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            Annotation[] annotations = parameterAnnotations[i];
            if (isAnnotationPresent(annotations, annotationClass)) {
                String name = getValue(annotations, annotationClass);
                if (StringUtils.isEmpty(name)) {
                    throw new SpringRestClientConfigurationException(
                            "REST client can't identify name by " + annotationClass);
                }
                parameters.put(name, i);
            }
        }
        return parameters;
    }

    private boolean isAnnotationPresent(Annotation[] annotations, Class<? extends Annotation> annotationClass) {
        for (Annotation annotation : annotations) {
            if (annotationClass.isInstance(annotation)) {
                return true;
            }
        }
        return false;
    }

    private String getValue(Annotation[] annotations, Class<? extends Annotation> annotationClass) {
        for (Annotation annotation : annotations) {
            if (annotationClass.isInstance(annotation)) {
                Method valueMethod = ReflectionUtils.findMethod(annotationClass, "value");
                if (valueMethod != null) {
                    return (String) ReflectionUtils.invokeMethod(valueMethod, annotation);
                } else {
                    return null;
                }
            }
        }

        return null;
    }
}