com.strandls.alchemy.rest.client.RestInterfaceAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for com.strandls.alchemy.rest.client.RestInterfaceAnalyzer.java

Source

/*
 * Copyright (C) 2015 Strand Life Sciences.
 *
 * 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 com.strandls.alchemy.rest.client;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import org.apache.commons.lang3.StringUtils;
import org.reflections.ReflectionUtils;

import com.google.common.base.Predicate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * Analyzes {@link Class} to extract {@link RestInterfaceMetadata}.
 *
 * @author Ashish Shinde
 *
 */
@Singleton
public class RestInterfaceAnalyzer {
    /**
     * The cache from class to rest metadata.
     */
    private final LoadingCache<Class<?>, RestInterfaceMetadata> metadataCache;

    /**
     * Creates a new instance.
     */
    public RestInterfaceAnalyzer() {
        metadataCache = CacheBuilder.newBuilder().build(new CacheLoader<Class<?>, RestInterfaceMetadata>() {

            @Override
            public RestInterfaceMetadata load(final Class<?> klass) throws Exception {
                return doAnalyze(klass);
            }
        });
    }

    /**
     * Analyze the class and build the meta information.
     *
     * @param klass
     *            the class.
     * @return analyzed metadata.
     */
    public RestInterfaceMetadata analyze(final Class<?> klass) throws NotRestInterfaceException {
        try {
            return metadataCache.getUnchecked(klass);
        } catch (final Exception e) {
            final Throwable cause = e.getCause();
            if (cause != null && cause instanceof NotRestInterfaceException) {
                throw (NotRestInterfaceException) cause;
            }
            throw e;
        }
    }

    /**
     * Get rest meta data for the method.
     *
     * @param method
     *            the method to analyze.
     * @return the metadata for a method.
     */
    private RestMethodMetadata analyzeMethod(final Method method) {
        String path = "";
        String httpMethod = null;
        final List<String> produced = new ArrayList<String>();
        final List<String> consumed = new ArrayList<String>();

        final Annotation[] annotations = method.getDeclaredAnnotations();
        for (final Annotation annotation : annotations) {
            if (annotation instanceof Path) {
                path = ((Path) annotation).value();
            } else if (annotation instanceof GET) {
                httpMethod = HttpMethod.GET;
            } else if (annotation instanceof PUT) {
                httpMethod = HttpMethod.PUT;
            } else if (annotation instanceof POST) {
                httpMethod = HttpMethod.POST;
            } else if (annotation instanceof DELETE) {
                httpMethod = HttpMethod.DELETE;
            } else if (annotation instanceof Produces) {
                final Produces produces = (Produces) annotation;
                produced.addAll(Arrays.asList(produces.value()));
            } else if (annotation instanceof Consumes) {
                final Consumes consumes = (Consumes) annotation;
                consumed.addAll(Arrays.asList(consumes.value()));
            }
        }

        if (StringUtils.isBlank(httpMethod)) {
            // no http method specified.
            return null;
        }
        return new RestMethodMetadata(path, httpMethod, produced, consumed, method.getParameterAnnotations());
    }

    /**
     * Analyze the class and build the meta information.
     *
     * @param klass
     *            the class.
     * @return analyzed metadata.
     * @throws NotRestInterfaceException
     *             if the class analyzed is not a rest interface.
     */
    protected RestInterfaceMetadata doAnalyze(final Class<?> klass) throws NotRestInterfaceException {
        final List<String> produced = new ArrayList<String>();
        final List<String> consumed = new ArrayList<String>();
        String path = null;

        @SuppressWarnings("unchecked")
        final Set<Annotation> declaredAnnotationList = ReflectionUtils.getAllAnnotations(klass);
        for (final Annotation annotation : declaredAnnotationList) {
            if (annotation instanceof Path) {
                path = ((Path) annotation).value();
            } else if (annotation instanceof Produces) {
                final Produces produces = (Produces) annotation;
                final String[] values = produces.value();
                produced.addAll(Arrays.asList(values));
            } else if (annotation instanceof Consumes) {
                final Consumes consumes = (Consumes) annotation;
                final String[] values = consumes.value();
                consumed.addAll(Arrays.asList(values));
            }
        }

        final Map<Method, RestMethodMetadata> methodMetadataMap = new LinkedHashMap<Method, RestMethodMetadata>();

        @SuppressWarnings("unchecked")
        final Set<Method> methods = ReflectionUtils.getAllMethods(klass, new Predicate<Method>() {
            @Override
            public boolean apply(final Method input) {
                // return only public methods
                return Modifier.isPublic(input.getModifiers());
            }
        });

        for (final Method method : methods) {
            final RestMethodMetadata methodMetadata = analyzeMethod(method);
            if (methodMetadata != null) {
                methodMetadataMap.put(method, methodMetadata);
            }
        }

        if (methodMetadataMap.isEmpty()) {
            // not a valid rest interface.
            throw new NotRestInterfaceException(klass);
        }

        return new RestInterfaceMetadata(path, produced, consumed, methodMetadataMap);
    }
}