1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.web.controller;
17
18 import org.springframework.util.LinkedMultiValueMap;
19 import org.springframework.util.MultiValueMap;
20 import org.springframework.web.method.HandlerMethod;
21 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
22 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
23
24 import javax.servlet.http.HttpServletRequest;
25 import java.lang.reflect.Method;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.LinkedHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35
36
37
38
39
40
41 public class UifRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
42
43 private final Map<RequestMappingInfo, HandlerMethod> handlerMethods =
44 new LinkedHashMap<RequestMappingInfo, HandlerMethod>();
45
46 private final MultiValueMap<String, RequestMappingInfo> urlMap =
47 new LinkedMultiValueMap<String, RequestMappingInfo>();
48
49
50
51
52 @Override
53 public Map<RequestMappingInfo, HandlerMethod> getHandlerMethods() {
54 return Collections.unmodifiableMap(this.handlerMethods);
55 }
56
57
58
59
60
61
62 @Override
63 protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
64 HandlerMethod newHandlerMethod = super.createHandlerMethod(handler, method);
65
66 this.handlerMethods.put(mapping, newHandlerMethod);
67 if (logger.isInfoEnabled()) {
68 logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
69 }
70
71 if (!this.handlerMethods.containsKey(mapping)) {
72 Set<String> patterns = super.getMappingPathPatterns(mapping);
73 for (String pattern : patterns) {
74 if (!super.getPathMatcher().isPattern(pattern)) {
75 this.urlMap.add(pattern, mapping);
76 }
77 }
78 }
79 }
80
81
82
83
84 @Override
85 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
86 List<Match> matches = new ArrayList<Match>();
87
88 List<RequestMappingInfo> directPathMatches = this.urlMap.get(lookupPath);
89 if (directPathMatches != null) {
90 addMatchingMappings(directPathMatches, matches, request);
91 }
92
93 if (matches.isEmpty()) {
94
95 addMatchingMappings(this.handlerMethods.keySet(), matches, request);
96 }
97
98 if (!matches.isEmpty()) {
99 Comparator<Match> comparator = new MatchComparator(super.getMappingComparator(request));
100 Collections.sort(matches, comparator);
101
102 if (logger.isTraceEnabled()) {
103 logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
104 }
105
106 Match bestMatch = matches.get(0);
107 if (matches.size() > 1) {
108 Match secondBestMatch = matches.get(1);
109 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
110 Method m1 = bestMatch.handlerMethod.getMethod();
111 Method m2 = secondBestMatch.handlerMethod.getMethod();
112 throw new IllegalStateException(
113 "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
114 m1 + ", " + m2 + "}");
115 }
116 }
117
118 handleMatch(bestMatch.mapping, lookupPath, request);
119 return bestMatch.handlerMethod;
120 } else {
121 return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
122 }
123 }
124
125
126 private void addMatchingMappings(Collection<RequestMappingInfo> mappings, List<Match> matches,
127 HttpServletRequest request) {
128 for (RequestMappingInfo mapping : mappings) {
129 RequestMappingInfo match = getMatchingMapping(mapping, request);
130 if (match != null) {
131 matches.add(new Match(match, handlerMethods.get(mapping)));
132 }
133 }
134 }
135
136 private static class Match {
137 private final RequestMappingInfo mapping;
138 private final HandlerMethod handlerMethod;
139
140 private Match(RequestMappingInfo mapping, HandlerMethod handlerMethod) {
141 this.mapping = mapping;
142 this.handlerMethod = handlerMethod;
143 }
144
145 @Override
146 public String toString() {
147 return this.mapping.toString();
148 }
149 }
150
151 private static class MatchComparator implements Comparator<Match> {
152 private final Comparator<RequestMappingInfo> comparator;
153
154 public MatchComparator(Comparator<RequestMappingInfo> comparator) {
155 this.comparator = comparator;
156 }
157
158 public int compare(Match match1, Match match2) {
159 return this.comparator.compare(match1.mapping, match2.mapping);
160 }
161 }
162 }