001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.method.annotation;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.core.MethodParameter;
023import org.springframework.util.Assert;
024import org.springframework.util.ClassUtils;
025import org.springframework.web.bind.support.WebArgumentResolver;
026import org.springframework.web.bind.support.WebDataBinderFactory;
027import org.springframework.web.context.request.NativeWebRequest;
028import org.springframework.web.method.support.HandlerMethodArgumentResolver;
029import org.springframework.web.method.support.ModelAndViewContainer;
030
031/**
032 * An abstract base class adapting a {@link WebArgumentResolver} to the
033 * {@link HandlerMethodArgumentResolver} contract.
034 *
035 * <p><strong>Note:</strong> This class is provided for backwards compatibility.
036 * However it is recommended to re-write a {@code WebArgumentResolver} as
037 * {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter}
038 * can only be implemented by actually resolving the value and then checking
039 * the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions
040 * raised must be absorbed and ignored since it's not clear whether the adapter
041 * doesn't support the parameter or whether it failed for an internal reason.
042 * The {@code HandlerMethodArgumentResolver} contract also provides access to
043 * model attributes and to {@code WebDataBinderFactory} (for type conversion).
044 *
045 * @author Arjen Poutsma
046 * @author Rossen Stoyanchev
047 * @since 3.1
048 */
049public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
050
051        private final Log logger = LogFactory.getLog(getClass());
052
053        private final WebArgumentResolver adaptee;
054
055
056        /**
057         * Create a new instance.
058         */
059        public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
060                Assert.notNull(adaptee, "'adaptee' must not be null");
061                this.adaptee = adaptee;
062        }
063
064
065        /**
066         * Actually resolve the value and check the resolved value is not
067         * {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions.
068         */
069        @Override
070        public boolean supportsParameter(MethodParameter parameter) {
071                try {
072                        NativeWebRequest webRequest = getWebRequest();
073                        Object result = this.adaptee.resolveArgument(parameter, webRequest);
074                        if (result == WebArgumentResolver.UNRESOLVED) {
075                                return false;
076                        }
077                        else {
078                                return ClassUtils.isAssignableValue(parameter.getParameterType(), result);
079                        }
080                }
081                catch (Exception ex) {
082                        // ignore (see class-level doc)
083                        if (logger.isDebugEnabled()) {
084                                logger.debug("Error in checking support for parameter [" + parameter + "]: " + ex.getMessage());
085                        }
086                        return false;
087                }
088        }
089
090        /**
091         * Delegate to the {@link WebArgumentResolver} instance.
092         * @throws IllegalStateException if the resolved value is not assignable
093         * to the method parameter.
094         */
095        @Override
096        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
097                        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
098
099                Class<?> paramType = parameter.getParameterType();
100                Object result = this.adaptee.resolveArgument(parameter, webRequest);
101                if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
102                        throw new IllegalStateException(
103                                        "Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
104                                        "resolved to incompatible value of type [" + (result != null ? result.getClass() : null) +
105                                        "]. Consider declaring the argument type in a less specific fashion.");
106                }
107                return result;
108        }
109
110
111        /**
112         * Required for access to NativeWebRequest in {@link #supportsParameter}.
113         */
114        protected abstract NativeWebRequest getWebRequest();
115
116}