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.messaging.handler.invocation;
018
019import java.util.Collections;
020import java.util.LinkedList;
021import java.util.List;
022import java.util.Map;
023import java.util.concurrent.ConcurrentHashMap;
024
025import org.springframework.core.MethodParameter;
026import org.springframework.messaging.Message;
027
028/**
029 * Resolves method parameters by delegating to a list of registered
030 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
031 * Previously resolved method parameters are cached for faster lookups.
032 *
033 * @author Rossen Stoyanchev
034 * @author Juergen Hoeller
035 * @since 4.0
036 */
037public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
038
039        private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<HandlerMethodArgumentResolver>();
040
041        private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
042                        new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);
043
044
045        /**
046         * Add the given {@link HandlerMethodArgumentResolver}.
047         */
048        public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) {
049                this.argumentResolvers.add(resolver);
050                return this;
051        }
052
053        /**
054         * Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
055         * @since 4.3
056         */
057        public HandlerMethodArgumentResolverComposite addResolvers(HandlerMethodArgumentResolver... resolvers) {
058                if (resolvers != null) {
059                        Collections.addAll(this.argumentResolvers, resolvers);
060                }
061                return this;
062        }
063
064        /**
065         * Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
066         */
067        public HandlerMethodArgumentResolverComposite addResolvers(List<? extends HandlerMethodArgumentResolver> resolvers) {
068                if (resolvers != null) {
069                        this.argumentResolvers.addAll(resolvers);
070                }
071                return this;
072        }
073
074        /**
075         * Return a read-only list with the contained resolvers, or an empty list.
076         */
077        public List<HandlerMethodArgumentResolver> getResolvers() {
078                return Collections.unmodifiableList(this.argumentResolvers);
079        }
080
081        /**
082         * Clear the list of configured resolvers.
083         */
084        public void clear() {
085                this.argumentResolvers.clear();
086        }
087
088
089        /**
090         * Whether the given {@linkplain MethodParameter method parameter} is
091         * supported by any registered {@link HandlerMethodArgumentResolver}.
092         */
093        @Override
094        public boolean supportsParameter(MethodParameter parameter) {
095                return getArgumentResolver(parameter) != null;
096        }
097
098        /**
099         * Iterate over registered
100         * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
101         * and invoke the one that supports it.
102         * @throws IllegalArgumentException if no suitable argument resolver is found
103         */
104        @Override
105        public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
106                HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
107                if (resolver == null) {
108                        throw new IllegalArgumentException("Unsupported parameter type [" +
109                                        parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
110                }
111                return resolver.resolveArgument(parameter, message);
112        }
113
114        /**
115         * Find a registered {@link HandlerMethodArgumentResolver} that supports
116         * the given method parameter.
117         */
118        private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
119                HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
120                if (result == null) {
121                        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
122                                if (resolver.supportsParameter(parameter)) {
123                                        result = resolver;
124                                        this.argumentResolverCache.put(parameter, result);
125                                        break;
126                                }
127                        }
128                }
129                return result;
130        }
131
132}