Line | Hits | Source |
---|---|---|
1 | /* | |
2 | Chrysalis Web Framework [http://chrysalis.sourceforge.net] | |
3 | Copyright (c) 2002, 2003, 2004, Paul Strack | |
4 | ||
5 | All rights reserved. | |
6 | ||
7 | Redistribution and use in source and binary forms, with or without | |
8 | modification, are permitted provided that the following conditions are met: | |
9 | ||
10 | 1. Redistributions of source code must retain the above copyright notice, this | |
11 | list of conditions and the following disclaimer. | |
12 | ||
13 | 2. Redistributions in binary form must reproduce the above copyright notice, | |
14 | this list of conditions and the following disclaimer in the documentation | |
15 | and/or other materials provided with the distribution. | |
16 | ||
17 | 3. Neither the name of the copyright holder nor the names of its contributors | |
18 | may be used to endorse or promote products derived from this software without | |
19 | specific prior written permission. | |
20 | ||
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | package org.chwf.servlet.engine; | |
34 | ||
35 | import java.lang.reflect.InvocationTargetException; | |
36 | import java.lang.reflect.Method; | |
37 | import java.util.HashMap; | |
38 | import java.util.Iterator; | |
39 | import java.util.LinkedList; | |
40 | import java.util.List; | |
41 | import java.util.Map; | |
42 | ||
43 | import javax.servlet.http.HttpServletRequest; | |
44 | ||
45 | import org.chwf.converter.ConversionException; | |
46 | import org.chwf.plugin.AuthenticationException; | |
47 | import org.chwf.plugin.User; | |
48 | import org.chwf.servlet.Controller; | |
49 | import org.chwf.servlet.ControllerException; | |
50 | import org.chwf.servlet.MissingParameterException; | |
51 | import org.chwf.servlet.MultiMissingParameterException; | |
52 | import org.chwf.servlet.MultiParameterException; | |
53 | import org.chwf.servlet.ParameterException; | |
54 | import org.chwf.util.MiscUtils; | |
55 | ||
56 | /** | |
57 | * Mapper for methods. It stores the information necessary to invoke | |
58 | * the method through reflection and pass it request data. Note that | |
59 | * this method uses the term "arguments" to refer to arguments of the | |
60 | * Java method being invoked, and reserves "parameters" to refer to | |
61 | * request parameters. | |
62 | * | |
63 | * @author <a href="mailto:pfstrack@users.sourceforge.net">Paul Strack</a> | |
64 | */ | |
65 | class MethodMapper { | |
66 | ||
67 | /** An empty string array. */ | |
68 | 1 | private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
69 | ||
70 | /** The initialization method prefix. */ | |
71 | private static final String INIT_METHOD_PREFIX = "get"; | |
72 | ||
73 | /** The controller class. */ | |
74 | private final Class controllerClass; | |
75 | ||
76 | /** The method being mapped. */ | |
77 | private final Method method; | |
78 | ||
79 | /** The method's default view. */ | |
80 | private final String defaultView; | |
81 | ||
82 | /** The method's default error page. */ | |
83 | private final String defaultErrorPage; | |
84 | ||
85 | /** Array of method roles, for security. */ | |
86 | private final String[] roles; | |
87 | ||
88 | /** Value indicating that the method is a bean initializer. */ | |
89 | private final boolean isInitializer; | |
90 | ||
91 | /** The method's argument mappers. */ | |
92 | private ArgumentMapper[] mappers; | |
93 | ||
94 | /** Method configuration. */ | |
95 | private final MethodConfig methodConfig; | |
96 | ||
97 | /** Controller mapper. */ | |
98 | private ControllerMapper controllerMapper; | |
99 | ||
100 | /** | |
101 | * Get an initialization (getter) method for the given parameter. This | |
102 | * method can only have simple (non-bean) parameters. | |
103 | * | |
104 | * @param parameter The request parameter. | |
105 | * @return A MethodMapper for the init method, or null if there is none. | |
106 | */ | |
107 | MethodMapper getInitMethod(String parameter) { | |
108 | try { | |
109 | 23 | String methodName = INIT_METHOD_PREFIX + MiscUtils.capitalize(parameter); |
110 | 23 | return this.controllerMapper.getMethodMapper(methodName); |
111 | } catch (ControllerException ex) { | |
112 | 15 | return null; |
113 | } | |
114 | } | |
115 | ||
116 | /** | |
117 | * Constructor for MethodMapper. | |
118 | * | |
119 | * @param methodConfig The method configuration. | |
120 | * @param controllerMapper The controller mapper. | |
121 | * @throws ControllerException If the method cannot be mapped. | |
122 | */ | |
123 | MethodMapper(MethodConfig methodConfig, ControllerMapper controllerMapper) | |
124 | 78 | throws ControllerException { |
125 | try { | |
126 | 78 | this.controllerMapper = controllerMapper; |
127 | 78 | this.methodConfig = methodConfig; |
128 | 78 | this.isInitializer = methodConfig.isInitializer(); |
129 | 78 | this.controllerClass = methodConfig.getController(); |
130 | 78 | this.method = methodConfig.getMethod(); |
131 | 78 | this.defaultView = methodConfig.getDefaultView(); |
132 | 78 | this.defaultErrorPage = methodConfig.getDefaultErrorPage(); |
133 | 78 | this.roles = methodConfig.getRoles(); |
134 | 78 | createArgumentMappers(method); |
135 | 75 | } catch (ControllerException ex) { |
136 | 3 | throw ex; |
137 | } catch (Exception ex) { | |
138 | 0 | Object[] args = |
139 | { this.method.getName(), this.controllerClass.getName(), ex }; | |
140 | 0 | throw new ControllerException( |
141 | ControllerMessages.MESSAGE_FAILED_METHOD_MAPPER_INIT, | |
142 | args); | |
143 | } | |
144 | 75 | } |
145 | ||
146 | /** | |
147 | * Whether the method is a bean initializer. | |
148 | * | |
149 | * @return Whether the method is a bean initializer. | |
150 | */ | |
151 | boolean isInitializer() { | |
152 | 28 | return isInitializer; |
153 | } | |
154 | ||
155 | /** | |
156 | * The method. | |
157 | * | |
158 | * @return The method. | |
159 | */ | |
160 | Method getMethod() { | |
161 | 8 | return method; |
162 | } | |
163 | ||
164 | /** | |
165 | * The method name. | |
166 | * | |
167 | * @return The method name. | |
168 | */ | |
169 | String getMethodName() { | |
170 | 79 | return this.method.getName(); |
171 | } | |
172 | ||
173 | /** | |
174 | * The controller class. | |
175 | * | |
176 | * @return The controller class. | |
177 | */ | |
178 | Class getControllerClass() { | |
179 | 3 | return this.controllerClass; |
180 | } | |
181 | ||
182 | /** | |
183 | * The default view for redirection. | |
184 | * | |
185 | * @return The default view for redirection. | |
186 | */ | |
187 | String getDefaultView() { | |
188 | 41 | return this.defaultView; |
189 | } | |
190 | ||
191 | /** | |
192 | * The default error page for error handling. | |
193 | * | |
194 | * @return The default error page for error handling. | |
195 | */ | |
196 | String getDefaultErrorPage() { | |
197 | 41 | return defaultErrorPage; |
198 | } | |
199 | ||
200 | /** | |
201 | * Invoke the mapped method, passing it parameter data from the request. | |
202 | * | |
203 | * @param request Must contain the correct parameter data. | |
204 | * @return The return value of the method. | |
205 | * @throws InvocationTargetException If the underlying method invocation | |
206 | * fails. | |
207 | * @throws ParameterException If there is a problem with a request parameter. | |
208 | * @throws ControllerException For general configuration problems. | |
209 | * @throws AuthenticationException With error message for security failure. | |
210 | */ | |
211 | Object invoke(HttpServletRequest request) | |
212 | throws | |
213 | InvocationTargetException, | |
214 | ParameterException, | |
215 | ControllerException, | |
216 | AuthenticationException { | |
217 | try { | |
218 | 46 | checkSecurity(); |
219 | 44 | return method.invoke(getController(), getArgumentValues(request)); |
220 | } catch (IllegalAccessException ex) { | |
221 | // This should not happen, but just in case ... | |
222 | 0 | Object[] args = { getMethodName(), getControllerClass().getName()}; |
223 | 0 | throw new ControllerException( |
224 | ControllerMessages.MESSAGE_METHOD_NOT_PUBLIC, | |
225 | new Object[] { getMethodName(), getControllerClass().getName()}); | |
226 | } | |
227 | } | |
228 | ||
229 | /** | |
230 | * Initialize arguments for this method. | |
231 | * | |
232 | * @param method The method object. | |
233 | * @throws ControllerException If argument mappers cannot be initialized. | |
234 | * @throws ConversionException If there a conversion failures. | |
235 | */ | |
236 | private void createArgumentMappers(Method method) | |
237 | throws ControllerException, ConversionException { | |
238 | 78 | Class[] types = method.getParameterTypes(); |
239 | 78 | String[] arguments = methodConfig.getParameterNames(); |
240 | 78 | Object[] defaults = methodConfig.getParameterDefaults(); |
241 | 78 | this.mappers = new ArgumentMapper[types.length]; |
242 | ||
243 | 107 | for (int i = 0; i < types.length; i++) { |
244 | 32 | this.mappers[i] = |
245 | ArgumentMapper.newInstance(arguments[i], types[i], defaults[i], this); | |
246 | } | |
247 | 75 | } |
248 | ||
249 | /** | |
250 | * Convert request data into objects via the argument mappers. | |
251 | * | |
252 | * @param request The request. | |
253 | * @return Request data as method argument objects. | |
254 | * @throws ParameterException With accumulated errors. | |
255 | * @throws MissingParameterException For missing parameters. | |
256 | */ | |
257 | private Object[] getArgumentValues(HttpServletRequest request) | |
258 | throws ParameterException, MissingParameterException { | |
259 | 44 | Object[] args = new Object[this.mappers.length]; |
260 | ||
261 | 44 | List missingParams = null; |
262 | 44 | Map invalidParams = null; |
263 | ||
264 | 70 | for (int i = 0; i < mappers.length; i++) { |
265 | try { | |
266 | 26 | args[i] = mappers[i].format(request); |
267 | 17 | } catch (MultiMissingParameterException ex) { |
268 | 3 | missingParams = addMissingParams(missingParams, ex); |
269 | 3 | } catch (MissingParameterException ex) { |
270 | 3 | missingParams = addMissingParams(missingParams, ex.getParameter()); |
271 | 3 | } catch (MultiParameterException ex) { |
272 | 2 | invalidParams = addInvalidParams(invalidParams, ex); |
273 | 2 | } catch (ParameterException ex) { |
274 | 1 | invalidParams = addInvalidParams(invalidParams, ex); |
275 | } | |
276 | } | |
277 | ||
278 | 44 | if (missingParams != null) { |
279 | 6 | throw new MultiMissingParameterException(missingParams); |
280 | } | |
281 | 38 | if (invalidParams != null) { |
282 | 3 | throw new MultiParameterException(invalidParams); |
283 | } | |
284 | 35 | return args; |
285 | } | |
286 | ||
287 | /** | |
288 | * Accumulate missing parameters. | |
289 | * | |
290 | * @param missingParams Running list of missing parameters. | |
291 | * @param ex With more missing parameters. | |
292 | * @return The updated list. | |
293 | */ | |
294 | private List addMissingParams( | |
295 | List missingParams, | |
296 | MultiMissingParameterException ex) { | |
297 | ||
298 | 3 | String[] parameters = ex.getParameters(); |
299 | 7 | for (int j = 0; j < parameters.length; j++) { |
300 | 4 | String name = parameters[j]; |
301 | 4 | missingParams = addMissingParams(missingParams, name); |
302 | } | |
303 | 3 | return missingParams; |
304 | } | |
305 | ||
306 | /** | |
307 | * Accumulate missing parameters. | |
308 | * | |
309 | * @param missingParams Running list of missing parameters. | |
310 | * @param name Additional missing parameter. | |
311 | * @return The updated list. | |
312 | */ | |
313 | private List addMissingParams(List missingParams, String name) { | |
314 | 7 | if (missingParams == null) { |
315 | 6 | missingParams = new LinkedList(); |
316 | } | |
317 | 7 | missingParams.add(name); |
318 | 7 | return missingParams; |
319 | } | |
320 | ||
321 | /** | |
322 | * Accumulate invalid parameters. | |
323 | * | |
324 | * @param invalidParams Running list of invalid parameters. | |
325 | * @param ex With more invalid parameters. | |
326 | * @return The updated list. | |
327 | */ | |
328 | private Map addInvalidParams(Map invalidParams, MultiParameterException ex) { | |
329 | 2 | Iterator it = ex.getParameterNames(); |
330 | 7 | while (it.hasNext()) { |
331 | 3 | String name = (String) it.next(); |
332 | 3 | String error = ex.getMessage(name); |
333 | 3 | invalidParams = addInvalidParams(invalidParams, name, error); |
334 | } | |
335 | 2 | return invalidParams; |
336 | } | |
337 | ||
338 | /** | |
339 | * Accumulate invalid parameters. | |
340 | * | |
341 | * @param invalidParams Running list of invalid parameters. | |
342 | * @param ex Another invalid parameters. | |
343 | * @return The updated list. | |
344 | */ | |
345 | private Map addInvalidParams(Map invalidParams, ParameterException ex) { | |
346 | 1 | return addInvalidParams(invalidParams, ex.getParameter(), ex.getMessage()); |
347 | } | |
348 | ||
349 | /** | |
350 | * Accumulate invalid parameters. | |
351 | * | |
352 | * @param invalidParams Running list of invalid parameters. | |
353 | * @param name Another invalid parameters. | |
354 | * @param error The error message. | |
355 | * @return The updated list. | |
356 | */ | |
357 | private Map addInvalidParams(Map invalidParams, String name, String error) { | |
358 | 4 | if (invalidParams == null) { |
359 | 3 | invalidParams = new HashMap(); |
360 | } | |
361 | 4 | invalidParams.put(name, error); |
362 | 4 | return invalidParams; |
363 | } | |
364 | ||
365 | /** | |
366 | * Get controller singleton. | |
367 | * | |
368 | * @return The controller singleton. | |
369 | * @throws ControllerException If the controller cannot be initialized. | |
370 | */ | |
371 | private Controller getController() throws ControllerException { | |
372 | 44 | return Controller.getController(this.controllerClass); |
373 | } | |
374 | ||
375 | /** | |
376 | * Check security constraints, to ensure that each user belongs to at least | |
377 | * one of the specified roles for this method. | |
378 | * | |
379 | * @throws AuthenticationException With error message for security failure. | |
380 | */ | |
381 | private void checkSecurity() throws AuthenticationException { | |
382 | 46 | if (this.roles.length > 0) { |
383 | 4 | User.getInstance().check(roles); |
384 | } | |
385 | 44 | } |
386 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |