Package org.springbyexample.mvc.method.annotation

Source Code of org.springbyexample.mvc.method.annotation.ServiceHandlerMapping

/*
* Copyright 2007-2013 the original author or authors.
*
* 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 org.springbyexample.mvc.method.annotation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springbyexample.converter.ListConverter;
import org.springbyexample.mvc.bind.annotation.RestRequestResource;
import org.springbyexample.mvc.bind.annotation.RestResource;
import org.springbyexample.mvc.converter.handler.ConverterHandlerInfo;
import org.springbyexample.mvc.converter.handler.ConverterHandlerInterceptor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;


/**
* Creates {@link RequestMappingInfo} instances from type and method-level
* {@link RequestMapping @RequestMapping} annotations in
* {@link @RestResource @RestResource} classes.
*
* @author David Winterfeldt
*/
public class ServiceHandlerMapping extends RequestMappingHandlerMapping {

    final Logger logger = LoggerFactory.getLogger(getClass());

    private List<String> basePackages;
    private RestServiceComponentProvider scanner;
    private ConverterHandlerInfo converterHandlerInfo;

    // temporary variable used to store the method
    // while registerHandlerMethod is called and overridden createHandlerMethod is called from it
    private Method restBridgedMethod;

    /**
     * Sets base packages to scan for <code>RestResource</code>s.
     */
    public void setBasePackages(List<String> basePackages) {
        this.basePackages = basePackages;
    }

    /**
     * Sets scanner.
     */
    public void setScanner(RestServiceComponentProvider scanner) {
        this.scanner = scanner;
    }

    /**
     * Sets converter handler info.
     */
    public void setConverterHandlerInfo(ConverterHandlerInfo converterHandlerInfo) {
        Assert.notNull(converterHandlerInfo);
        Assert.notNull(converterHandlerInfo.getPropertyName());

        this.converterHandlerInfo = converterHandlerInfo;
    }

    @Override
    protected void initHandlerMethods() {
        for (String basePackage : basePackages) {
            Collection<BeanDefinition> components = scanner.findCandidateComponents(basePackage);

            for (final BeanDefinition definition : components) {
                final Class<?> marshallingServiceClass;

                try {
                    marshallingServiceClass = ClassUtils.forName(definition.getBeanClassName(), ClassUtils.getDefaultClassLoader());
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("Error loading interface class.", e);
                } catch (LinkageError e) {
                    throw new RuntimeException("Error loading interface class.", e);
                }

                Set<Method> methods = HandlerMethodSelector.selectMethods(marshallingServiceClass, new MethodFilter() {
                    @Override
                    public boolean matches(Method method) {
                        return (getMappingForMethod(method, marshallingServiceClass) != null);
                    }
                });

                for (Method method : methods) {
                    registerHandler(marshallingServiceClass, method);
                }
            }
        }

        initInterceptors();
        handlerMethodsInitialized(getHandlerMethods());
    }

    /**
     * Register handler.
     */
    private void registerHandler(Class<?> marshallingServiceClass, Method method) {
        ApplicationContext ctx = getApplicationContext();

        RestResource restResource = AnnotationUtils.findAnnotation(marshallingServiceClass, RestResource.class);
        Class<?> serviceClass = restResource.service();

        RestRequestResource restRequestResource = AnnotationUtils.findAnnotation(method, RestRequestResource.class);
        boolean export = (restRequestResource != null ? restRequestResource.export() : true);
        boolean relative = (restRequestResource != null ? restRequestResource.relative() : true);

        if (export) {
            Class<?> handlerServiceClass = serviceClass;
            String methodName = method.getName();
            Class<?>[] paramTypes = method.getParameterTypes();

            if (restRequestResource != null) {
                // explicit service specified
                if (restRequestResource.service() != ServiceValueConstants.DEFAULT_SERVICE_CLASS) {
                    handlerServiceClass = restRequestResource.service();
                }

                // explicit method name specified
                if (StringUtils.hasText(restRequestResource.methodName())) {
                    methodName = restRequestResource.methodName();
                }
            }

            Object handler = ctx.getBean(handlerServiceClass);
            Method serviceMethod = ClassUtils.getMethod(handlerServiceClass, methodName, paramTypes);
            RequestMappingInfo mapping = getMappingForMethod(method, marshallingServiceClass);

            if (relative) {
                List<String> patterns = new ArrayList<String>();

                for (String pattern : mapping.getPatternsCondition().getPatterns()) {
                    // add REST resource path prefix to URI,
                    // if relative path is just '/' add an empty string
                    patterns.add(restResource.path() + (!"/".equals(pattern) ? pattern : ""));
                }

                // create a new mapping based on the patterns (patterns are unmodifiable in existing RequestMappingInfo)
                mapping = new RequestMappingInfo(
                            new PatternsRequestCondition(patterns.toArray(ArrayUtils.EMPTY_STRING_ARRAY),
                                                         getUrlPathHelper(), getPathMatcher(),
                                                         useSuffixPatternMatch(), useTrailingSlashMatch()),
                            mapping.getMethodsCondition(), mapping.getParamsCondition(), mapping.getHeadersCondition(),
                            mapping.getConsumesCondition(), mapping.getProducesCondition(), mapping.getCustomCondition());
            }

            // need to set param types to use in createHandlerMethod before calling registerHandlerMethod
            restBridgedMethod = BridgeMethodResolver.findBridgedMethod(method);

            // mapping is key, HandlerMethod is value
            registerHandlerMethod(handler, serviceMethod, mapping);

            processConverters(restRequestResource, mapping, serviceMethod);
        }
    }

    @Override
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        HandlerMethod handlerMethod = super.createHandlerMethod(handler, method);

        if (restBridgedMethod != null) {
            handlerMethod = new ServiceHandlerMethod(handler, method, restBridgedMethod);
        }

        return handlerMethod;
    }

    /**
     * Process and setup any converter handlers if one is configured on <code>RestRequestResource</code>.
     */
    private void processConverters(RestRequestResource restRequestResource, RequestMappingInfo mapping,
                                   Method serviceMethod) {
        ApplicationContext ctx = getApplicationContext();
        Class<?> converterClass = (restRequestResource != null ? restRequestResource.converter() : null);

        if (converterClass != null && converterClass != ServiceValueConstants.DEFAULT_CONVERTER_CLASS) {
            @SuppressWarnings("rawtypes")
            ListConverter converter = (ListConverter) ctx.getBean(converterClass);

            String[] pathPatterns = mapping.getPatternsCondition().getPatterns().toArray(ArrayUtils.EMPTY_STRING_ARRAY);

            String methodSuffix = StringUtils.capitalize(converterHandlerInfo.getPropertyName());
            String getterMethodName = "get" + methodSuffix;
            final String setterMethodName = "set" + methodSuffix;

            final Class<?> returnTypeClass = serviceMethod.getReturnType();
            Method getResultsMethod = ReflectionUtils.findMethod(returnTypeClass, getterMethodName);
            final Class<?> resultReturnTypeClass = getResultsMethod.getReturnType();
            Method setResultsMethod = ReflectionUtils.findMethod(returnTypeClass, setterMethodName, resultReturnTypeClass);
            final AtomicReference<Method> altSetResultsMethod = new AtomicReference<Method>();

            // issue with ReflectionUtils, setterResultsMethod sometimes null from the command line (not getter?)
            if (setResultsMethod == null) {
                ReflectionUtils.doWithMethods(returnTypeClass, new MethodCallback() {
                    @Override
                    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                        if (setterMethodName.equals(method.getName())) {
                            altSetResultsMethod.set(method);
                        logger.debug("Unable to use ReflectionUtils to find setter. returnTypeClass={}  method={} resultReturnTypeClass={}",
                                new Object[] { returnTypeClass, method, resultReturnTypeClass });
                        }
                    }
                });
            }

            HandlerInterceptor interceptor = new ConverterHandlerInterceptor(converter, returnTypeClass,
                    getResultsMethod, (setResultsMethod != null ? setResultsMethod : altSetResultsMethod.get()));

            MappedInterceptor mappedInterceptor = new MappedInterceptor(pathPatterns, interceptor);
            setInterceptors(new Object[]{ mappedInterceptor });

            logger.info("Registered converter post handler for {} with {}.", pathPatterns, converterClass.getCanonicalName());
        }
    }

}
TOP

Related Classes of org.springbyexample.mvc.method.annotation.ServiceHandlerMapping

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.