1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.method.support;
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.Arrays;
22
23 import org.springframework.core.DefaultParameterNameDiscoverer;
24 import org.springframework.core.GenericTypeResolver;
25 import org.springframework.core.MethodParameter;
26 import org.springframework.core.ParameterNameDiscoverer;
27 import org.springframework.util.ReflectionUtils;
28 import org.springframework.web.bind.WebDataBinder;
29 import org.springframework.web.bind.support.SessionStatus;
30 import org.springframework.web.bind.support.WebDataBinderFactory;
31 import org.springframework.web.context.request.NativeWebRequest;
32 import org.springframework.web.method.HandlerMethod;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class InvocableHandlerMethod extends HandlerMethod {
50
51 private WebDataBinderFactory dataBinderFactory;
52
53 private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
54
55 private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
56
57
58
59
60
61 public InvocableHandlerMethod(Object bean, Method method) {
62 super(bean, method);
63 }
64
65
66
67
68 public InvocableHandlerMethod(HandlerMethod handlerMethod) {
69 super(handlerMethod);
70 }
71
72
73
74
75
76
77
78
79 public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
80 throws NoSuchMethodException {
81
82 super(bean, methodName, parameterTypes);
83 }
84
85
86
87
88
89
90
91 public void setDataBinderFactory(WebDataBinderFactory dataBinderFactory) {
92 this.dataBinderFactory = dataBinderFactory;
93 }
94
95
96
97
98 public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
99 this.argumentResolvers = argumentResolvers;
100 }
101
102
103
104
105
106
107 public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
108 this.parameterNameDiscoverer = parameterNameDiscoverer;
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
127 Object... providedArgs) throws Exception {
128
129 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
130 if (logger.isTraceEnabled()) {
131 StringBuilder sb = new StringBuilder("Invoking [");
132 sb.append(getBeanType().getSimpleName()).append(".");
133 sb.append(getMethod().getName()).append("] method with arguments ");
134 sb.append(Arrays.asList(args));
135 logger.trace(sb.toString());
136 }
137 Object returnValue = doInvoke(args);
138 if (logger.isTraceEnabled()) {
139 logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
140 }
141 return returnValue;
142 }
143
144
145
146
147 private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
148 Object... providedArgs) throws Exception {
149
150 MethodParameter[] parameters = getMethodParameters();
151 Object[] args = new Object[parameters.length];
152 for (int i = 0; i < parameters.length; i++) {
153 MethodParameter parameter = parameters[i];
154 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
155 GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
156 args[i] = resolveProvidedArgument(parameter, providedArgs);
157 if (args[i] != null) {
158 continue;
159 }
160 if (this.argumentResolvers.supportsParameter(parameter)) {
161 try {
162 args[i] = this.argumentResolvers.resolveArgument(
163 parameter, mavContainer, request, this.dataBinderFactory);
164 continue;
165 }
166 catch (Exception ex) {
167 if (logger.isTraceEnabled()) {
168 logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
169 }
170 throw ex;
171 }
172 }
173 if (args[i] == null) {
174 String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
175 throw new IllegalStateException(msg);
176 }
177 }
178 return args;
179 }
180
181 private String getArgumentResolutionErrorMessage(String message, int index) {
182 MethodParameter param = getMethodParameters()[index];
183 message += " [" + index + "] [type=" + param.getParameterType().getName() + "]";
184 return getDetailedErrorMessage(message);
185 }
186
187
188
189
190
191 protected String getDetailedErrorMessage(String message) {
192 StringBuilder sb = new StringBuilder(message).append("\n");
193 sb.append("HandlerMethod details: \n");
194 sb.append("Controller [").append(getBeanType().getName()).append("]\n");
195 sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
196 return sb.toString();
197 }
198
199
200
201
202 private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
203 if (providedArgs == null) {
204 return null;
205 }
206 for (Object providedArg : providedArgs) {
207 if (parameter.getParameterType().isInstance(providedArg)) {
208 return providedArg;
209 }
210 }
211 return null;
212 }
213
214
215
216
217
218 protected Object doInvoke(Object... args) throws Exception {
219 ReflectionUtils.makeAccessible(getBridgedMethod());
220 try {
221 return getBridgedMethod().invoke(getBean(), args);
222 }
223 catch (IllegalArgumentException ex) {
224 assertTargetBean(getBridgedMethod(), getBean(), args);
225 throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
226 }
227 catch (InvocationTargetException ex) {
228
229 Throwable targetException = ex.getTargetException();
230 if (targetException instanceof RuntimeException) {
231 throw (RuntimeException) targetException;
232 }
233 else if (targetException instanceof Error) {
234 throw (Error) targetException;
235 }
236 else if (targetException instanceof Exception) {
237 throw (Exception) targetException;
238 }
239 else {
240 String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
241 throw new IllegalStateException(msg, targetException);
242 }
243 }
244 }
245
246
247
248
249
250
251
252
253 private void assertTargetBean(Method method, Object targetBean, Object[] args) {
254 Class<?> methodDeclaringClass = method.getDeclaringClass();
255 Class<?> targetBeanClass = targetBean.getClass();
256 if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
257 String msg = "The mapped controller method class '" + methodDeclaringClass.getName() +
258 "' is not an instance of the actual controller bean instance '" +
259 targetBeanClass.getName() + "'. If the controller requires proxying " +
260 "(e.g. due to @Transactional), please use class-based proxying.";
261 throw new IllegalStateException(getInvocationErrorMessage(msg, args));
262 }
263 }
264
265 private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
266 StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
267 sb.append("Resolved arguments: \n");
268 for (int i=0; i < resolvedArgs.length; i++) {
269 sb.append("[").append(i).append("] ");
270 if (resolvedArgs[i] == null) {
271 sb.append("[null] \n");
272 }
273 else {
274 sb.append("[type=").append(resolvedArgs[i].getClass().getName()).append("] ");
275 sb.append("[value=").append(resolvedArgs[i]).append("]\n");
276 }
277 }
278 return sb.toString();
279 }
280
281 }