1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.dispatcher;
22
23 import java.io.Serializable;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.HashMap;
27
28 import org.apache.struts.action.Action;
29 import org.apache.struts.chain.contexts.ActionContext;
30 import org.apache.struts.config.ActionConfig;
31 import org.apache.struts.util.MessageResources;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36
37
38
39
40
41
42 public abstract class AbstractDispatcher implements Dispatcher, Serializable {
43 private static final long serialVersionUID = 8527912438873600103L;
44
45
46 static final String LOCAL_STRINGS = "org.apache.struts.dispatcher.LocalStrings";
47 static final String MSG_KEY_DISPATCH_ERROR = "dispatcher.error";
48 static final String MSG_KEY_MISSING_METHOD = "dispatcher.missingMethod";
49 static final String MSG_KEY_MISSING_METHOD_LOG = "dispatcher.missingMethod.log";
50 static final String MSG_KEY_MISSING_MAPPING_PARAMETER = "dispatcher.missingMappingParameter";
51 static final String MSG_KEY_UNSPECIFIED = "dispatcher.unspecified";
52
53
54
55
56
57
58 public static final String CANCELLED_METHOD_NAME = "cancelled";
59
60
61
62
63 public static final String EXECUTE_METHOD_NAME = "execute";
64
65
66
67
68 static MessageResources messages = MessageResources.getMessageResources(LOCAL_STRINGS);
69
70
71
72
73 private transient final Logger log =
74 LoggerFactory.getLogger(AbstractDispatcher.class);
75
76
77
78
79
80
81
82 private transient final HashMap<String, Method> methods;
83
84 private final MethodResolver methodResolver;
85
86
87
88
89
90
91 public AbstractDispatcher(MethodResolver methodResolver) {
92 this.methodResolver = methodResolver;
93 methods = new HashMap<>();
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107 Object[] buildMethodArguments(ActionContext context, Method method) {
108 Object[] args = methodResolver.buildArguments(context, method);
109 if (args == null) {
110 throw new IllegalStateException("Unsupported method signature: " + method.toString());
111 }
112 return args;
113 }
114
115 public Object dispatch(ActionContext context) throws Exception {
116
117 String methodName = resolveMethodName(context);
118 if ((methodName == null) || "".equals(methodName)) {
119 methodName = getDefaultMethodName();
120 }
121
122
123
124 if (methodName == null) {
125 return unspecified(context);
126 }
127
128
129 Method method;
130 try {
131 method = getMethod(context, methodName);
132 } catch (NoSuchMethodException e) {
133
134 String path = context.getActionConfig().getPath();
135 if (log.isErrorEnabled()) {
136 String message = messages.getMessage(MSG_KEY_MISSING_METHOD_LOG, path, methodName);
137 log.error(message, e);
138 }
139
140
141
142 String userMsg = messages.getMessage(MSG_KEY_MISSING_METHOD, path);
143 NoSuchMethodException e2 = new NoSuchMethodException(userMsg);
144 e2.initCause(e);
145 throw e2;
146 }
147
148
149 return dispatchMethod(context, method, methodName);
150 }
151
152
153
154
155
156
157
158
159
160
161
162 protected final Object dispatchMethod(ActionContext context, Method method, String name) throws Exception {
163 Action target = context.getAction();
164 String path = context.getActionConfig().getPath();
165 Object[] args = buildMethodArguments(context, method);
166 return invoke(target, method, args, path);
167 }
168
169
170
171
172
173
174 final void flushMethodCache() {
175 synchronized (methods) {
176 methods.clear();
177 }
178 }
179
180
181
182
183
184
185
186
187
188
189 protected String getDefaultMethodName() {
190 return EXECUTE_METHOD_NAME;
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205 protected final Method getMethod(ActionContext context, String methodName) throws NoSuchMethodException {
206 synchronized (methods) {
207
208 StringBuilder keyBuf = new StringBuilder(100);
209 keyBuf.append(context.getAction().getClass().getName());
210 keyBuf.append(":");
211 keyBuf.append(methodName);
212 String key = keyBuf.toString();
213
214 Method method = methods.get(key);
215
216 if (method == null) {
217 method = resolveMethod(context, methodName);
218 methods.put(key, method);
219 }
220
221 return method;
222 }
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236 protected final Object invoke(Object target, Method method, Object[] args, String path) throws Exception {
237 try {
238 Object retval = method.invoke(target, args);
239 if (method.getReturnType() == void.class) {
240 retval = void.class;
241 }
242 return retval;
243 } catch (IllegalAccessException e) {
244 log.atError()
245 .setMessage("{}:{}")
246 .addArgument(() -> messages.getMessage(MSG_KEY_DISPATCH_ERROR, path))
247 .addArgument(method.getName())
248 .setCause(e).log();
249 throw e;
250 } catch (InvocationTargetException e) {
251
252
253 Throwable t = e.getTargetException();
254 if (t instanceof Exception) {
255 throw (Exception) t;
256 } else {
257 log.atError()
258 .setMessage("{}:{}")
259 .addArgument(() -> messages.getMessage(MSG_KEY_DISPATCH_ERROR, path))
260 .addArgument(method.getName())
261 .setCause(e).log();
262 throw new Exception(t);
263 }
264 }
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278 protected boolean isCancelled(ActionContext context) {
279 Boolean cancelled = context.getCancelled();
280 return (cancelled != null) && cancelled.booleanValue();
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException {
297 return methodResolver.resolveMethod(context, methodName);
298 }
299
300
301
302
303
304
305
306
307
308 abstract String resolveMethodName(ActionContext context);
309
310
311
312
313
314
315
316
317
318
319
320
321 protected Object unspecified(ActionContext context) throws Exception {
322 ActionConfig config = context.getActionConfig();
323 String msg = messages.getMessage(MSG_KEY_UNSPECIFIED, config.getPath());
324 log.error(msg);
325 throw new IllegalStateException(msg);
326 }
327 }