1. /*
  2. * Copyright 2002-2004 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.mvc;
  17. import javax.servlet.http.HttpServletRequest;
  18. import org.springframework.validation.BindException;
  19. import org.springframework.validation.MessageCodesResolver;
  20. import org.springframework.validation.ValidationUtils;
  21. import org.springframework.validation.Validator;
  22. import org.springframework.web.bind.ServletRequestDataBinder;
  23. /**
  24. * <p>Controller implementation which creates an object (the command object) on
  25. * receipt of a request and attempts to populate this object with request parameters.</p>
  26. *
  27. * <p>This controller is the base for all controllers wishing to populate
  28. * JavaBeans based on request parameters, validate the content of such
  29. * JavaBeans using {@link org.springframework.validation.Validator Validators}
  30. * and use custom editors (in the form of
  31. * {@link java.beans.PropertyEditor PropertyEditors}) to transform
  32. * objects into strings and vice versa, for example. Three notions are mentioned here:</p>
  33. *
  34. * <p><b>Command class:</b><br>
  35. * An instance of the command class will be created for each request and populated
  36. * with request parameters. A command class can basically be any Java class; the only
  37. * requirement is a no-arg constructor. The command class should preferably be a
  38. * JavaBean in order to be able to populate bean properties with request parameters.</p>
  39. *
  40. * <p><b>Populating using request parameters and PropertyEditors:</b><br>
  41. * Upon receiving a request, any BaseCommandController will attempt to fill the
  42. * command object using the request parameters. This is done using the typical
  43. * and well-known JavaBeans property notation. When a request parameter named
  44. * <code>'firstName'</code> exists, the framework will attempt to call
  45. * <code>setFirstName([value])</code> passing the value of the parameter. Nested properties
  46. * are of course supported. For instance a parameter named <code>'address.city'</code>
  47. * will result in a <code>getAddress().setCity([value])</code> call on the
  48. * command class.</p>
  49. *
  50. * <p>It's important to realise that you are not limited to String arguments in
  51. * your JavaBeans. Using the PropertyEditor-notion as supplied by the
  52. * java.beans package, you will be able to transform Strings to Objects and
  53. * the other way around. For instance <code>setLocale(Locale loc)</code> is
  54. * perfectly possible for a request parameter named <code>locale</code> having
  55. * a value of <code>en</code>, as long as you register the appropriate
  56. * PropertyEditor in the Controller (see {@link #initBinder initBinder()}
  57. * for more information on that matter.</p>
  58. *
  59. * <p><b>Validators:</b>
  60. * After the controller has successfully populated the command object with
  61. * parameters from the request, it will use any configured validators to
  62. * validate the object. Validation results will be put in a
  63. * {@link org.springframework.validation.Errors Errors} object which can be
  64. * used in a View to render any input problems.</p>
  65. *
  66. * <p><b><a name="workflow">Workflow
  67. * (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
  68. * Since this class is an abstract base class for more specific implementation,
  69. * it does not override the handleRequestInternal() method and also has no
  70. * actual workflow. Implementing classes like
  71. * {@link AbstractFormController AbstractFormController},
  72. * {@link AbstractCommandController AbstractcommandController},
  73. * {@link SimpleFormController SimpleFormController} and
  74. * {@link AbstractWizardFormController AbstractWizardFormController}
  75. * provide actual functionality and workflow.
  76. * More information on workflow performed by superclasses can be found
  77. * <a href="AbstractController.html#workflow">here</a>.</p>
  78. *
  79. * <p><b><a name="config">Exposed configuration properties</a>
  80. * (<a href="AbstractController.html#config">and those defined by superclass</a>):</b><br>
  81. * <table border="1">
  82. * <tr>
  83. * <td><b>name</b></th>
  84. * <td><b>default</b></td>
  85. * <td><b>description</b></td>
  86. * </tr>
  87. * <tr>
  88. * <td>commandName</td>
  89. * <td>command</td>
  90. * <td>the name to use when binding the instantiated command class
  91. * to the request</td>
  92. * </tr>
  93. * <tr>
  94. * <td>commandClass</td>
  95. * <td><i>null</i></td>
  96. * <td>the class to use upon receiving a request and which to fill
  97. * using the request parameters. What object is used and whether
  98. * or not it should be created is defined by extending classes
  99. * and their configuration properties and methods.</td>
  100. * </tr>
  101. * <tr>
  102. * <td>validator</td>
  103. * <td><i>null</i></td>
  104. * <td>Validator bean (usually passed in using a <ref bean="beanId"/>
  105. * property. The validator will be called at appropriate places in the
  106. * workflow of subclasses (have a look at those for more info) to
  107. * validate the command object.</td>
  108. * </tr>
  109. * <tr>
  110. * <td>validateOnBinding</td>
  111. * <td>true</td>
  112. * <td>Indicates whether or not to validate the command object after the
  113. * object has been populated with request parameters.</td>
  114. * </tr>
  115. * </table>
  116. * </p>
  117. *
  118. * @author Rod Johnson
  119. * @author Juergen Hoeller
  120. */
  121. public abstract class BaseCommandController extends AbstractController {
  122. public static final String DEFAULT_COMMAND_NAME = "command";
  123. private String commandName = DEFAULT_COMMAND_NAME;
  124. private Class commandClass;
  125. private Validator[] validators;
  126. private boolean validateOnBinding = true;
  127. private MessageCodesResolver messageCodesResolver;
  128. /**
  129. * Set the name of the command in the model.
  130. * The command object will be included in the model under this name.
  131. */
  132. public final void setCommandName(String commandName) {
  133. this.commandName = commandName;
  134. }
  135. /**
  136. * Return the name of the command in the model.
  137. */
  138. protected final String getCommandName() {
  139. return this.commandName;
  140. }
  141. /**
  142. * Set the command class for this controller.
  143. * An instance of this class gets populated and validated on each request.
  144. */
  145. public final void setCommandClass(Class commandClass) {
  146. this.commandClass = commandClass;
  147. }
  148. /**
  149. * Return the command class for this controller.
  150. */
  151. protected final Class getCommandClass() {
  152. return this.commandClass;
  153. }
  154. /**
  155. * Set the primary Validator for this controller.
  156. * The Validator must support the specified command class.
  157. */
  158. public final void setValidators(Validator[] validators) {
  159. this.validators = validators;
  160. }
  161. /**
  162. * Return the Validators for this controller.
  163. */
  164. protected final Validator[] getValidators() {
  165. return validators;
  166. }
  167. /**
  168. * Set the Validators for this controller.
  169. * The Validator must support the specified command class.
  170. */
  171. public final void setValidator(Validator validator) {
  172. this.validators = new Validator[] {validator};
  173. }
  174. /**
  175. * Return the primary Validator for this controller.
  176. */
  177. protected final Validator getValidator() {
  178. return (validators != null && validators.length > 0 ? validators[0] : null);
  179. }
  180. /**
  181. * Set if the Validator should get applied when binding.
  182. */
  183. public final void setValidateOnBinding(boolean validateOnBinding) {
  184. this.validateOnBinding = validateOnBinding;
  185. }
  186. /**
  187. * Return if the Validator should get applied when binding.
  188. */
  189. protected final boolean isValidateOnBinding() {
  190. return validateOnBinding;
  191. }
  192. /**
  193. * Set the strategy to use for resolving errors into message codes.
  194. * Applies the given strategy to all data binders used by this controller.
  195. * <p>Default is null, i.e. using the default strategy of the data binder.
  196. * @see #createBinder
  197. * @see org.springframework.validation.DataBinder#setMessageCodesResolver
  198. */
  199. public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
  200. this.messageCodesResolver = messageCodesResolver;
  201. }
  202. /**
  203. * Return the strategy to use for resolving errors into message codes.
  204. */
  205. protected final MessageCodesResolver getMessageCodesResolver() {
  206. return messageCodesResolver;
  207. }
  208. protected void initApplicationContext() {
  209. if (this.validators != null) {
  210. for (int i = 0; i < this.validators.length; i++) {
  211. if (this.commandClass != null && !this.validators[i].supports(this.commandClass))
  212. throw new IllegalArgumentException("Validator [" + this.validators[i] +
  213. "] does not support command class [" +
  214. this.commandClass.getName() + "]");
  215. }
  216. }
  217. }
  218. /**
  219. * Retrieve a command object for the given request.
  220. * <p>Default implementation calls createCommand. Subclasses can override this.
  221. * @param request current HTTP request
  222. * @return object command to bind onto
  223. * @see #createCommand
  224. */
  225. protected Object getCommand(HttpServletRequest request) throws Exception {
  226. return createCommand();
  227. }
  228. /**
  229. * Create a new command instance for the command class of this controller.
  230. * @return the new command instance
  231. * @throws InstantiationException if the command class could not be instantiated
  232. * @throws IllegalAccessException if the class or its constructor is not accessible
  233. */
  234. protected final Object createCommand() throws InstantiationException, IllegalAccessException {
  235. if (this.commandClass == null) {
  236. throw new IllegalStateException("Cannot create command without commandClass being set - " +
  237. "either set commandClass or override formBackingObject");
  238. }
  239. logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
  240. return this.commandClass.newInstance();
  241. }
  242. /**
  243. * Check if the given command object is a valid for this controller,
  244. * i.e. its command class.
  245. * @param command command object to check
  246. * @return if the command object is valid for this controller
  247. */
  248. protected final boolean checkCommand(Object command) {
  249. return (this.commandClass == null || this.commandClass.isInstance(command));
  250. }
  251. /**
  252. * Bind the parameters of the given request to the given command object.
  253. * @param request current HTTP request
  254. * @param command command to bind onto
  255. * @return the ServletRequestDataBinder instance for additional custom validation
  256. * @throws Exception in case of invalid state or arguments
  257. */
  258. protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)
  259. throws Exception {
  260. ServletRequestDataBinder binder = createBinder(request, command);
  261. binder.bind(request);
  262. onBind(request, command, binder.getErrors());
  263. if (isValidateOnBinding() && this.validators != null) {
  264. for (int i = 0; i < this.validators.length; i++) {
  265. ValidationUtils.invokeValidator(this.validators[i], command, binder.getErrors());
  266. }
  267. }
  268. onBindAndValidate(request, command, binder.getErrors());
  269. return binder;
  270. }
  271. /**
  272. * Create a new binder instance for the given command and request.
  273. * Called by bindAndValidate. Can be overridden to plug in custom
  274. * ServletRequestDataBinder subclasses.
  275. * <p>Default implementation creates a standard ServletRequestDataBinder,
  276. * sets the specified MessageCodesResolver (if any), and invokes initBinder.
  277. * Note that initBinder will not be invoked if you override this method!
  278. * @param command command to bind onto
  279. * @param request current request
  280. * @return the new binder instance
  281. * @throws Exception in case of invalid state or arguments
  282. * @see #bindAndValidate
  283. * @see #initBinder
  284. * @see #setMessageCodesResolver
  285. */
  286. protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command)
  287. throws Exception {
  288. ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName());
  289. if (this.messageCodesResolver != null) {
  290. binder.setMessageCodesResolver(this.messageCodesResolver);
  291. }
  292. initBinder(request, binder);
  293. return binder;
  294. }
  295. /**
  296. * Initialize the given binder instance, for example with custom editors.
  297. * Called by createBinder.
  298. * <p>This method allows you to register custom editors for certain fields of your
  299. * command class. For instance, you will be able to transform Date objects into a
  300. * String pattern and back, in order to allow your JavaBeans to have Date properties
  301. * and still be able to set and display them in an HTML interface.
  302. * <p>Default implementation is empty.
  303. * @param request current request
  304. * @param binder new binder instance
  305. * @throws Exception in case of invalid state or arguments
  306. * @see #createBinder
  307. * @see org.springframework.validation.DataBinder#registerCustomEditor
  308. * @see org.springframework.beans.propertyeditors.CustomDateEditor
  309. */
  310. protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
  311. throws Exception {
  312. }
  313. /**
  314. * Callback for custom post-processing in terms of binding.
  315. * Called on each submit, after standard binding but before validation.
  316. * <p>Default implementation delegates to onBind(request, command).
  317. * @param request current HTTP request
  318. * @param command command object to perform further binding on
  319. * @param errors validation errors holder, allowing for additional
  320. * custom registration of binding errors
  321. * @throws Exception in case of invalid state or arguments
  322. * @see #bindAndValidate
  323. * @see #onBind(HttpServletRequest, Object)
  324. */
  325. protected void onBind(HttpServletRequest request, Object command, BindException errors)
  326. throws Exception {
  327. onBind(request, command);
  328. }
  329. /**
  330. * Callback for custom post-processing in terms of binding.
  331. * Called by the default implementation of the onBind version with
  332. * all parameters, after standard binding but before validation.
  333. * <p>Default implementation is empty.
  334. * @param request current HTTP request
  335. * @param command command object to perform further binding on
  336. * @throws Exception in case of invalid state or arguments
  337. * @see #onBind(HttpServletRequest, Object, BindException)
  338. */
  339. protected void onBind(HttpServletRequest request, Object command) throws Exception {
  340. }
  341. /**
  342. * Callback for custom post-processing in terms of binding and validation.
  343. * Called on each submit, after standard binding and validation,
  344. * but before error evaluation.
  345. * <p>Default implementation is empty.
  346. * @param request current HTTP request
  347. * @param command command object, still allowing for further binding
  348. * @param errors validation errors holder, allowing for additional
  349. * custom validation
  350. * @throws Exception in case of invalid state or arguments
  351. * @see #bindAndValidate
  352. * @see org.springframework.validation.Errors
  353. */
  354. protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)
  355. throws Exception {
  356. }
  357. }