1
+ /*
2
+ * Copyright 2002-2016 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ package org .springframework .web .servlet .support ;
17
+
18
+ import java .io .IOException ;
19
+ import java .util .ArrayList ;
20
+ import java .util .List ;
21
+ import java .util .Map ;
22
+ import java .util .Properties ;
23
+ import javax .servlet .http .HttpServletRequest ;
24
+ import javax .servlet .http .HttpServletRequestWrapper ;
25
+
26
+ import org .springframework .beans .factory .BeanFactoryUtils ;
27
+ import org .springframework .context .ApplicationContext ;
28
+ import org .springframework .core .annotation .AnnotationAwareOrderComparator ;
29
+ import org .springframework .core .io .ClassPathResource ;
30
+ import org .springframework .core .io .Resource ;
31
+ import org .springframework .core .io .support .PropertiesLoaderUtils ;
32
+ import org .springframework .util .ClassUtils ;
33
+ import org .springframework .util .StringUtils ;
34
+ import org .springframework .web .cors .CorsConfiguration ;
35
+ import org .springframework .web .cors .CorsConfigurationSource ;
36
+ import org .springframework .web .servlet .DispatcherServlet ;
37
+ import org .springframework .web .servlet .HandlerExecutionChain ;
38
+ import org .springframework .web .servlet .HandlerInterceptor ;
39
+ import org .springframework .web .servlet .HandlerMapping ;
40
+
41
+ /**
42
+ * Helper class to get information from the {@code HandlerMapping} that would
43
+ * serve a specific request.
44
+ *
45
+ * <p>Provides the following methods:
46
+ * <ul>
47
+ * <li>{@link #getMatchableHandlerMapping} -- obtain a {@code HandlerMapping}
48
+ * to check request-matching criteria against.
49
+ * <li>{@link #getCorsConfiguration} -- obtain the CORS configuration for the
50
+ * request.
51
+ * </ul>
52
+ *
53
+ * @author Rossen Stoyanchev
54
+ * @since 4.3
55
+ */
56
+ public class HandlerMappingIntrospector implements CorsConfigurationSource {
57
+
58
+ private final List <HandlerMapping > handlerMappings ;
59
+
60
+
61
+ /**
62
+ * Constructor that detects the configured {@code HandlerMapping}s in the
63
+ * given {@code ApplicationContext} or falling back on
64
+ * "DispatcherServlet.properties" like the {@code DispatcherServlet}.
65
+ */
66
+ public HandlerMappingIntrospector (ApplicationContext context ) {
67
+ this .handlerMappings = initHandlerMappings (context );
68
+ }
69
+
70
+
71
+ private static List <HandlerMapping > initHandlerMappings (ApplicationContext context ) {
72
+
73
+ Map <String , HandlerMapping > beans = BeanFactoryUtils .beansOfTypeIncludingAncestors (
74
+ context , HandlerMapping .class , true , false );
75
+
76
+ if (!beans .isEmpty ()) {
77
+ List <HandlerMapping > mappings = new ArrayList <HandlerMapping >(beans .values ());
78
+ AnnotationAwareOrderComparator .sort (mappings );
79
+ return mappings ;
80
+ }
81
+
82
+ return initDefaultHandlerMappings (context );
83
+ }
84
+
85
+ private static List <HandlerMapping > initDefaultHandlerMappings (ApplicationContext context ) {
86
+ Properties props ;
87
+ String path = "DispatcherServlet.properties" ;
88
+ try {
89
+ Resource resource = new ClassPathResource (path , DispatcherServlet .class );
90
+ props = PropertiesLoaderUtils .loadProperties (resource );
91
+ }
92
+ catch (IOException ex ) {
93
+ throw new IllegalStateException ("Could not load '" + path + "': " + ex .getMessage ());
94
+ }
95
+
96
+ String value = props .getProperty (HandlerMapping .class .getName ());
97
+ String [] names = StringUtils .commaDelimitedListToStringArray (value );
98
+ List <HandlerMapping > result = new ArrayList <HandlerMapping >(names .length );
99
+ for (String name : names ) {
100
+ try {
101
+ Class <?> clazz = ClassUtils .forName (name , DispatcherServlet .class .getClassLoader ());
102
+ Object mapping = context .getAutowireCapableBeanFactory ().createBean (clazz );
103
+ result .add ((HandlerMapping ) mapping );
104
+ }
105
+ catch (ClassNotFoundException ex ) {
106
+ throw new IllegalStateException ("Could not find default HandlerMapping [" + name + "]" );
107
+ }
108
+ }
109
+ return result ;
110
+ }
111
+
112
+
113
+ /**
114
+ * Return the configured HandlerMapping's.
115
+ */
116
+ public List <HandlerMapping > getHandlerMappings () {
117
+ return this .handlerMappings ;
118
+ }
119
+
120
+
121
+ /**
122
+ * Find the {@link HandlerMapping} that would handle the given request and
123
+ * return it as a {@link MatchableHandlerMapping} that can be used to
124
+ * test request-matching criteria. If the matching HandlerMapping is not an
125
+ * instance of {@link MatchableHandlerMapping}, an IllegalStateException is
126
+ * raised.
127
+ *
128
+ * @param request the current request
129
+ * @return the resolved matcher, or {@code null}
130
+ * @throws Exception if any of the HandlerMapping's raise an exception
131
+ */
132
+ public MatchableHandlerMapping getMatchableHandlerMapping (HttpServletRequest request ) throws Exception {
133
+ HttpServletRequest wrapper = new RequestAttributeChangeIgnoringWrapper (request );
134
+ for (HandlerMapping handlerMapping : this .handlerMappings ) {
135
+ Object handler = handlerMapping .getHandler (wrapper );
136
+ if (handler == null ) {
137
+ continue ;
138
+ }
139
+ if (handlerMapping instanceof MatchableHandlerMapping ) {
140
+ return ((MatchableHandlerMapping ) handlerMapping );
141
+ }
142
+ throw new IllegalStateException ("HandlerMapping is not a MatchableHandlerMapping" );
143
+ }
144
+ return null ;
145
+ }
146
+
147
+ @ Override
148
+ public CorsConfiguration getCorsConfiguration (HttpServletRequest request ) {
149
+ HttpServletRequest wrapper = new RequestAttributeChangeIgnoringWrapper (request );
150
+ for (HandlerMapping handlerMapping : this .handlerMappings ) {
151
+ HandlerExecutionChain handler = null ;
152
+ try {
153
+ handler = handlerMapping .getHandler (wrapper );
154
+ }
155
+ catch (Exception ex ) {
156
+ // Ignore
157
+ }
158
+ if (handler == null ) {
159
+ continue ;
160
+ }
161
+ if (handler .getInterceptors () != null ) {
162
+ for (HandlerInterceptor interceptor : handler .getInterceptors ()) {
163
+ if (interceptor instanceof CorsConfigurationSource ) {
164
+ return ((CorsConfigurationSource ) interceptor ).getCorsConfiguration (wrapper );
165
+ }
166
+ }
167
+ }
168
+ if (handler .getHandler () instanceof CorsConfigurationSource ) {
169
+ return ((CorsConfigurationSource ) handler .getHandler ()).getCorsConfiguration (wrapper );
170
+ }
171
+ }
172
+ return null ;
173
+ }
174
+
175
+
176
+ /**
177
+ * Request wrapper that ignores request attribute changes.
178
+ */
179
+ private static class RequestAttributeChangeIgnoringWrapper extends HttpServletRequestWrapper {
180
+
181
+
182
+ private RequestAttributeChangeIgnoringWrapper (HttpServletRequest request ) {
183
+ super (request );
184
+ }
185
+
186
+ @ Override
187
+ public void setAttribute (String name , Object value ) {
188
+ // Ignore attribute change
189
+ }
190
+ }
191
+
192
+ }
0 commit comments