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.lang.Nullable;
024import org.springframework.util.Assert;
025import org.springframework.util.ClassUtils;
026import org.springframework.web.bind.support.WebArgumentResolver;
027import org.springframework.web.bind.support.WebDataBinderFactory;
028import org.springframework.web.context.request.NativeWebRequest;
029import org.springframework.web.method.support.HandlerMethodArgumentResolver;
030import org.springframework.web.method.support.ModelAndViewContainer;
031
032/**
033 * An abstract base class adapting a {@link WebArgumentResolver} to the
034 * {@link HandlerMethodArgumentResolver} contract.
035 *
036 * <p><strong>Note:</strong> This class is provided for backwards compatibility.
037 * However it is recommended to re-write a {@code WebArgumentResolver} as
038 * {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter}
039 * can only be implemented by actually resolving the value and then checking
040 * the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions
041 * raised must be absorbed and ignored since it's not clear whether the adapter
042 * doesn't support the parameter or whether it failed for an internal reason.
043 * The {@code HandlerMethodArgumentResolver} contract also provides access to
044 * model attributes and to {@code WebDataBinderFactory} (for type conversion).
045 *
046 * @author Arjen Poutsma
047 * @author Rossen Stoyanchev
048 * @since 3.1
049 */
050public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
051
052        private final Log logger = LogFactory.getLog(getClass());
053
054        private final WebArgumentResolver adaptee;
055
056
057        /**
058         * Create a new instance.
059         */
060        public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
061                Assert.notNull(adaptee, "'adaptee' must not be null");
062                this.adaptee = adaptee;
063        }
064
065
066        /**
067         * Actually resolve the value and check the resolved value is not
068         * {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions.
069         */
070        @Override
071        public boolean supportsParameter(MethodParameter parameter) {
072                try {
073                        NativeWebRequest webRequest = getWebRequest();
074                        Object result = this.adaptee.resolveArgument(parameter, webRequest);
075                        if (result == WebArgumentResolver.UNRESOLVED) {
076                                return false;
077                        }
078                        else {
079                                return ClassUtils.isAssignableValue(parameter.getParameterType(), result);
080                        }
081                }
082                catch (Exception ex) {
083                        // ignore (see class-level doc)
084                        if (logger.isDebugEnabled()) {
085                                logger.debug("Error in checking support for parameter [" + parameter + "]: " + ex.getMessage());
086                        }
087                        return false;
088                }
089        }
090
091        /**
092         * Delegate to the {@link WebArgumentResolver} instance.
093         * @throws IllegalStateException if the resolved value is not assignable
094         * to the method parameter.
095         */
096        @Override
097        @Nullable
098        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
099                        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
100
101                Class<?> paramType = parameter.getParameterType();
102                Object result = this.adaptee.resolveArgument(parameter, webRequest);
103                if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
104                        throw new IllegalStateException(
105                                        "Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
106                                        "resolved to incompatible value of type [" + (result != null ? result.getClass() : null) +
107                                        "]. Consider declaring the argument type in a less specific fashion.");
108                }
109                return result;
110        }
111
112
113        /**
114         * Required for access to NativeWebRequest in {@link #supportsParameter}.
115         */
116        protected abstract NativeWebRequest getWebRequest();
117
118}