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.servlet.config;
018
019import java.util.LinkedHashMap;
020import java.util.Map;
021
022import org.springframework.beans.factory.config.BeanDefinition;
023import org.springframework.beans.factory.config.RuntimeBeanReference;
024import org.springframework.beans.factory.parsing.BeanComponentDefinition;
025import org.springframework.beans.factory.support.RootBeanDefinition;
026import org.springframework.beans.factory.xml.ParserContext;
027import org.springframework.util.AntPathMatcher;
028import org.springframework.util.PathMatcher;
029import org.springframework.web.cors.CorsConfiguration;
030import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
031import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
032import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
033import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
034import org.springframework.web.util.UrlPathHelper;
035
036/**
037 * Convenience methods for use in MVC namespace BeanDefinitionParsers.
038 *
039 * @author Rossen Stoyanchev
040 * @author Brian Clozel
041 * @since 3.1
042 */
043public abstract class MvcNamespaceUtils {
044
045        private static final String BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME =
046                        BeanNameUrlHandlerMapping.class.getName();
047
048        private static final String SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME =
049                        SimpleControllerHandlerAdapter.class.getName();
050
051        private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME =
052                        HttpRequestHandlerAdapter.class.getName();
053
054        private static final String URL_PATH_HELPER_BEAN_NAME = "mvcUrlPathHelper";
055
056        private static final String PATH_MATCHER_BEAN_NAME = "mvcPathMatcher";
057
058        private static final String CORS_CONFIGURATION_BEAN_NAME = "mvcCorsConfigurations";
059
060        private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
061
062
063        public static void registerDefaultComponents(ParserContext parserContext, Object source) {
064                registerBeanNameUrlHandlerMapping(parserContext, source);
065                registerHttpRequestHandlerAdapter(parserContext, source);
066                registerSimpleControllerHandlerAdapter(parserContext, source);
067                registerHandlerMappingIntrospector(parserContext, source);
068        }
069
070        /**
071         * Adds an alias to an existing well-known name or registers a new instance of a {@link UrlPathHelper}
072         * under that well-known name, unless already registered.
073         * @return a RuntimeBeanReference to this {@link UrlPathHelper} instance
074         */
075        public static RuntimeBeanReference registerUrlPathHelper(
076                        RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, Object source) {
077
078                if (urlPathHelperRef != null) {
079                        if (parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) {
080                                parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME);
081                        }
082                        parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME);
083                }
084                else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) &&
085                                !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) {
086                        RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class);
087                        urlPathHelperDef.setSource(source);
088                        urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
089                        parserContext.getRegistry().registerBeanDefinition(URL_PATH_HELPER_BEAN_NAME, urlPathHelperDef);
090                        parserContext.registerComponent(new BeanComponentDefinition(urlPathHelperDef, URL_PATH_HELPER_BEAN_NAME));
091                }
092                return new RuntimeBeanReference(URL_PATH_HELPER_BEAN_NAME);
093        }
094
095        /**
096         * Adds an alias to an existing well-known name or registers a new instance of a {@link PathMatcher}
097         * under that well-known name, unless already registered.
098         * @return a RuntimeBeanReference to this {@link PathMatcher} instance
099         */
100        public static RuntimeBeanReference registerPathMatcher(
101                        RuntimeBeanReference pathMatcherRef, ParserContext parserContext, Object source) {
102
103                if (pathMatcherRef != null) {
104                        if (parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) {
105                                parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME);
106                        }
107                        parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME);
108                }
109                else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) &&
110                                !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
111                        RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class);
112                        pathMatcherDef.setSource(source);
113                        pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
114                        parserContext.getRegistry().registerBeanDefinition(PATH_MATCHER_BEAN_NAME, pathMatcherDef);
115                        parserContext.registerComponent(new BeanComponentDefinition(pathMatcherDef, PATH_MATCHER_BEAN_NAME));
116                }
117                return new RuntimeBeanReference(PATH_MATCHER_BEAN_NAME);
118        }
119
120        /**
121         * Registers  an {@link HttpRequestHandlerAdapter} under a well-known
122         * name unless already registered.
123         */
124        private static void registerBeanNameUrlHandlerMapping(ParserContext context, Object source) {
125                if (!context.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)) {
126                        RootBeanDefinition mappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);
127                        mappingDef.setSource(source);
128                        mappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
129                        mappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport
130                        RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
131                        mappingDef.getPropertyValues().add("corsConfigurations", corsRef);
132                        context.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, mappingDef);
133                        context.registerComponent(new BeanComponentDefinition(mappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME));
134                }
135        }
136
137        /**
138         * Registers  an {@link HttpRequestHandlerAdapter} under a well-known
139         * name unless already registered.
140         */
141        private static void registerHttpRequestHandlerAdapter(ParserContext context, Object source) {
142                if (!context.getRegistry().containsBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)) {
143                        RootBeanDefinition adapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
144                        adapterDef.setSource(source);
145                        adapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
146                        context.getRegistry().registerBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME, adapterDef);
147                        context.registerComponent(new BeanComponentDefinition(adapterDef, HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME));
148                }
149        }
150
151        /**
152         * Registers a {@link SimpleControllerHandlerAdapter} under a well-known
153         * name unless already registered.
154         */
155        private static void registerSimpleControllerHandlerAdapter(ParserContext context, Object source) {
156                if (!context.getRegistry().containsBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)) {
157                        RootBeanDefinition beanDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
158                        beanDef.setSource(source);
159                        beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
160                        context.getRegistry().registerBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME, beanDef);
161                        context.registerComponent(new BeanComponentDefinition(beanDef, SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME));
162                }
163        }
164
165        /**
166         * Registers a {@code Map<String, CorsConfiguration>} (mapped {@code CorsConfiguration}s)
167         * under a well-known name unless already registered. The bean definition may be updated
168         * if a non-null CORS configuration is provided.
169         * @return a RuntimeBeanReference to this {@code Map<String, CorsConfiguration>} instance
170         */
171        public static RuntimeBeanReference registerCorsConfigurations(
172                        Map<String, CorsConfiguration> corsConfigurations, ParserContext context, Object source) {
173
174                if (!context.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) {
175                        RootBeanDefinition corsDef = new RootBeanDefinition(LinkedHashMap.class);
176                        corsDef.setSource(source);
177                        corsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
178                        if (corsConfigurations != null) {
179                                corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
180                        }
181                        context.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsDef);
182                        context.registerComponent(new BeanComponentDefinition(corsDef, CORS_CONFIGURATION_BEAN_NAME));
183                }
184                else if (corsConfigurations != null) {
185                        BeanDefinition corsDef = context.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME);
186                        corsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
187                }
188                return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
189        }
190
191        /**
192         * Registers  an {@link HandlerMappingIntrospector} under a well-known name
193         * unless already registered.
194         */
195        private static void registerHandlerMappingIntrospector(ParserContext parserContext, Object source) {
196                if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
197                        RootBeanDefinition beanDef = new RootBeanDefinition(HandlerMappingIntrospector.class);
198                        beanDef.setSource(source);
199                        beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
200                        beanDef.setLazyInit(true);
201                        parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, beanDef);
202                        parserContext.registerComponent(new BeanComponentDefinition(beanDef, HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME));
203                }
204        }
205
206        /**
207         * Find the {@code ContentNegotiationManager} bean created by or registered
208         * with the {@code annotation-driven} element.
209         * @return a bean definition, bean reference, or {@code null} if none defined
210         */
211        public static Object getContentNegotiationManager(ParserContext context) {
212                String name = AnnotationDrivenBeanDefinitionParser.HANDLER_MAPPING_BEAN_NAME;
213                if (context.getRegistry().containsBeanDefinition(name)) {
214                        BeanDefinition handlerMappingBeanDef = context.getRegistry().getBeanDefinition(name);
215                        return handlerMappingBeanDef.getPropertyValues().get("contentNegotiationManager");
216                }
217                name = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME;
218                if (context.getRegistry().containsBeanDefinition(name)) {
219                        return new RuntimeBeanReference(name);
220                }
221                return null;
222        }
223
224}