1 16 package org.springframework.webflow.action; 17 18 import java.lang.reflect.Method ; 19 20 import org.springframework.beans.MutablePropertyValues; 21 import org.springframework.beans.PropertyEditorRegistrar; 22 import org.springframework.beans.PropertyEditorRegistry; 23 import org.springframework.beans.factory.InitializingBean; 24 import org.springframework.beans.propertyeditors.PropertiesEditor; 25 import org.springframework.core.style.StylerUtils; 26 import org.springframework.util.Assert; 27 import org.springframework.util.ClassUtils; 28 import org.springframework.util.StringUtils; 29 import org.springframework.validation.BindException; 30 import org.springframework.validation.DataBinder; 31 import org.springframework.validation.Errors; 32 import org.springframework.validation.MessageCodesResolver; 33 import org.springframework.validation.Validator; 34 import org.springframework.web.bind.WebDataBinder; 35 import org.springframework.webflow.execution.Event; 36 import org.springframework.webflow.execution.RequestContext; 37 import org.springframework.webflow.execution.ScopeType; 38 import org.springframework.webflow.util.DispatchMethodInvoker; 39 import org.springframework.webflow.util.ReflectionUtils; 40 41 276 public class FormAction extends MultiAction implements InitializingBean { 277 278 282 283 287 private static boolean hasPropertyEditorRegistryAccessor; 288 289 static { 290 hasPropertyEditorRegistryAccessor = ClassUtils 291 .hasMethod(BindException.class, "getPropertyEditorRegistry", null); 292 } 293 294 297 public static final String DEFAULT_FORM_OBJECT_NAME = "formObject"; 298 299 304 public static final String VALIDATOR_METHOD_ATTRIBUTE = "validatorMethod"; 305 306 310 private String formObjectName = DEFAULT_FORM_OBJECT_NAME; 311 312 317 private Class formObjectClass; 318 319 323 private ScopeType formObjectScope = ScopeType.FLOW; 324 325 329 private ScopeType formErrorsScope = ScopeType.FLASH; 330 331 336 private PropertyEditorRegistrar propertyEditorRegistrar; 337 338 341 private Validator validator; 342 343 346 private MessageCodesResolver messageCodesResolver; 347 348 351 private DispatchMethodInvoker validateMethodInvoker; 352 353 360 public FormAction() { 361 } 362 363 368 public FormAction(Class formObjectClass) { 369 setFormObjectClass(formObjectClass); 370 } 371 372 375 public String getFormObjectName() { 376 return formObjectName; 377 } 378 379 383 public void setFormObjectName(String formObjectName) { 384 this.formObjectName = formObjectName; 385 } 386 387 390 public Class getFormObjectClass() { 391 return formObjectClass; 392 } 393 394 404 public void setFormObjectClass(Class formObjectClass) { 405 this.formObjectClass = formObjectClass; 406 if ((formObjectName == null || formObjectName == DEFAULT_FORM_OBJECT_NAME) && formObjectClass != null) { 408 formObjectName = ClassUtils.getShortNameAsProperty(formObjectClass); 409 } 410 } 411 412 415 public ScopeType getFormObjectScope() { 416 return formObjectScope; 417 } 418 419 423 public void setFormObjectScope(ScopeType scopeType) { 424 this.formObjectScope = scopeType; 425 } 426 427 430 public ScopeType getFormErrorsScope() { 431 return formErrorsScope; 432 } 433 434 438 public void setFormErrorsScope(ScopeType errorsScope) { 439 this.formErrorsScope = errorsScope; 440 } 441 442 446 public PropertyEditorRegistrar getPropertyEditorRegistrar() { 447 return propertyEditorRegistrar; 448 } 449 450 455 public void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) { 456 this.propertyEditorRegistrar = propertyEditorRegistrar; 457 } 458 459 462 public Validator getValidator() { 463 return validator; 464 } 465 466 471 public void setValidator(Validator validator) { 472 this.validator = validator; 473 } 474 475 478 public MessageCodesResolver getMessageCodesResolver() { 479 return messageCodesResolver; 480 } 481 482 490 public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { 491 this.messageCodesResolver = messageCodesResolver; 492 } 493 494 protected void initAction() { 495 if (getValidator() != null) { 496 Assert.notNull(getFormObjectClass(), "When using a validator, the form object class is required"); 497 if (!getValidator().supports(getFormObjectClass())) { 498 throw new IllegalArgumentException ("Validator [" + getValidator() 499 + "] does not support form object class [" + getFormObjectClass() + "]"); 500 } 501 validateMethodInvoker = new DispatchMethodInvoker(validator, new Class [] { getFormObjectClass(), 503 Errors.class }); 504 } 505 } 506 507 509 527 public Event setupForm(RequestContext context) throws Exception { 528 if (logger.isDebugEnabled()) { 529 logger.debug("Executing setupForm"); 530 } 531 Object formObject = getFormObject(context); 533 if (!formErrorsExposed(context, formObject)) { 535 initFormErrors(context, formObject); 538 } 539 else { 540 reinstallPropertyEditors(context); 542 } 543 return success(); 544 } 545 546 564 public Event bindAndValidate(RequestContext context) throws Exception { 565 if (logger.isDebugEnabled()) { 566 logger.debug("Executing bind"); 567 } 568 Object formObject = getFormObject(context); 569 DataBinder binder = createBinder(context, formObject); 570 doBind(context, binder); 571 if (getValidator() != null && validationEnabled(context)) { 572 if (logger.isDebugEnabled()) { 573 logger.debug("Executing validation"); 574 } 575 doValidate(context, formObject, binder.getErrors()); 576 } 577 else { 578 if (logger.isDebugEnabled()) { 579 if (validator == null) { 580 logger.debug("No validator is configured, no validation will occur after binding"); 581 } 582 else { 583 logger.debug("Validation was disabled for this bindAndValidate request"); 584 } 585 } 586 } 587 putFormErrors(context, binder.getErrors()); 588 return binder.getErrors().hasErrors() ? error() : success(); 589 } 590 591 606 public Event bind(RequestContext context) throws Exception { 607 if (logger.isDebugEnabled()) { 608 logger.debug("Executing bind"); 609 } 610 Object formObject = getFormObject(context); 611 DataBinder binder = createBinder(context, formObject); 612 doBind(context, binder); 613 putFormErrors(context, binder.getErrors()); 614 return binder.getErrors().hasErrors() ? error() : success(); 615 } 616 617 633 public Event validate(RequestContext context) throws Exception { 634 if (getValidator() != null && validationEnabled(context)) { 635 if (logger.isDebugEnabled()) { 636 logger.debug("Executing validation"); 637 } 638 Object formObject = getFormObject(context); 639 Errors errors = getFormErrors(context); 640 doValidate(context, formObject, errors); 641 return errors.hasErrors() ? error() : success(); 642 } 643 else { 644 if (logger.isDebugEnabled()) { 645 if (validator == null) { 646 logger.debug("No validator is configured, no validation will occur"); 647 } 648 else { 649 logger.debug("Validation was disabled for this request"); 650 } 651 } 652 return success(); 653 } 654 } 655 656 669 public Event resetForm(RequestContext context) throws Exception { 670 Object formObject = initFormObject(context); 671 initFormErrors(context, formObject); 672 return success(); 673 } 674 675 677 684 private Object initFormObject(RequestContext context) throws Exception { 685 if (logger.isDebugEnabled()) { 686 logger.debug("Creating new form object with name '" + getFormObjectName() + "'"); 687 } 688 Object formObject = createFormObject(context); 689 putFormObject(context, formObject); 690 return formObject; 691 } 692 693 696 private void putFormObject(RequestContext context, Object formObject) { 697 if (logger.isDebugEnabled()) { 698 logger.debug("Putting form object of type [" + formObject.getClass() + "] in scope " + getFormObjectScope() 699 + " with name '" + getFormObjectName() + "'"); 700 } 701 getFormObjectAccessor(context).putFormObject(formObject, getFormObjectName(), getFormObjectScope()); 702 } 703 704 712 private Errors initFormErrors(RequestContext context, Object formObject) { 713 if (logger.isDebugEnabled()) { 714 logger.debug("Creating new form errors for object with name '" + getFormObjectName() + "'"); 715 } 716 Errors errors = createBinder(context, formObject).getErrors(); 717 putFormErrors(context, errors); 718 return errors; 719 } 720 721 724 private void putFormErrors(RequestContext context, Errors errors) { 725 if (logger.isDebugEnabled()) { 726 logger.debug("Putting form errors instance in scope " + getFormErrorsScope()); 727 } 728 getFormObjectAccessor(context).putFormErrors(errors, getFormErrorsScope()); 729 } 730 731 735 private boolean formErrorsExposed(RequestContext context, Object formObject) { 736 Errors errors = getFormObjectAccessor(context).getFormErrors(getFormObjectName(), getFormErrorsScope()); 737 if (errors instanceof BindException) { 738 BindException be = (BindException)errors; 741 if (be.getTarget() != formObject) { 742 if (logger.isInfoEnabled()) { 743 logger.info("Inconsistency detected: the Errors instance in '" + getFormErrorsScope() 744 + "' does NOT wrap the current form object '" + formObject + "' of class " 745 + formObject.getClass() 746 + "; instead this Errors instance unexpectedly wraps the target object '" + be.getTarget() 747 + "' of class: " + be.getTarget().getClass() + ". "); 748 } 749 return false; } 751 } 752 return errors != null; 753 } 754 755 759 private void reinstallPropertyEditors(RequestContext context) { 760 BindException errors = (BindException) 761 getFormObjectAccessor(context).getFormErrors(getFormObjectName(), getFormErrorsScope()); 762 registerPropertyEditors(context, getPropertyEditorRegistry(errors)); 763 } 764 765 769 private PropertyEditorRegistry getPropertyEditorRegistry(BindException errors) { 770 Method accessor; 771 try { 772 if (hasPropertyEditorRegistryAccessor) { 773 accessor = errors.getClass().getMethod("getPropertyEditorRegistry", null); 774 } 775 else { 776 accessor = errors.getClass().getDeclaredMethod("getBeanWrapper", null); 778 accessor.setAccessible(true); 779 } 780 } 781 catch (NoSuchMethodException e) { 782 throw new IllegalStateException ( 783 "Unable to resolve property editor registry accessor method as expected - this should not happen"); 784 } 785 return (PropertyEditorRegistry)ReflectionUtils.invokeMethod(accessor, errors); 786 } 787 788 800 private void invokeValidatorMethod(String validatorMethod, Object formObject, Errors errors) throws Exception { 801 if (logger.isDebugEnabled()) { 802 logger.debug("Invoking piecemeal validator method '" + validatorMethod + "(" + getFormObjectClass() 803 + ", Errors)'"); 804 } 805 getValidateMethodInvoker().invoke(validatorMethod, new Object [] { formObject, errors }); 806 } 807 808 810 823 protected final Object getFormObject(RequestContext context) throws Exception { 824 FormObjectAccessor accessor = getFormObjectAccessor(context); 825 Object formObject = accessor.getFormObject(getFormObjectName(), getFormObjectScope()); 826 if (formObject == null) { 827 formObject = initFormObject(context); 828 } 829 else { 830 if (logger.isDebugEnabled()) { 831 logger.debug("Found existing form object with name '" + getFormObjectName() + "' of type [" 832 + formObject.getClass() + "] in scope " + getFormObjectScope()); 833 } 834 accessor.setCurrentFormObject(formObject, getFormObjectScope()); 835 } 836 return formObject; 837 } 838 839 852 protected final Errors getFormErrors(RequestContext context) throws Exception { 853 Object formObject = getFormObject(context); 854 if (!formErrorsExposed(context, formObject)) { 855 initFormErrors(context, formObject); 856 } 857 return getFormObjectAccessor(context).getFormErrors(getFormObjectName(), getFormErrorsScope()); 858 } 859 860 874 protected DataBinder createBinder(RequestContext context, Object formObject) { 875 DataBinder binder = new WebDataBinder(formObject, getFormObjectName()); 876 if (messageCodesResolver != null) { 877 binder.setMessageCodesResolver(messageCodesResolver); 878 } 879 initBinder(context, binder); 880 registerPropertyEditors(context, binder); 881 return binder; 882 } 883 884 891 protected void doBind(RequestContext context, DataBinder binder) { 892 if (logger.isDebugEnabled()) { 893 logger.debug("Binding allowed request parameters in " 894 + StylerUtils.style(context.getExternalContext().getRequestParameterMap()) 895 + " to form object with name '" + binder.getObjectName() + "', pre-bind formObject toString = " 896 + binder.getTarget()); 897 if (binder.getAllowedFields() != null && binder.getAllowedFields().length > 0) { 898 logger.debug("(Allowed fields are " + StylerUtils.style(binder.getAllowedFields()) + ")"); 899 } 900 else { 901 logger.debug("(Any field is allowed)"); 902 } 903 } 904 binder.bind(new MutablePropertyValues(context.getRequestParameters().asMap())); 905 if (logger.isDebugEnabled()) { 906 logger.debug("Binding completed for form object with name '" + binder.getObjectName() 907 + "', post-bind formObject toString = " + binder.getTarget()); 908 logger.debug("There are [" + binder.getErrors().getErrorCount() + "] errors, details: " 909 + binder.getErrors().getAllErrors()); 910 } 911 } 912 913 925 protected void doValidate(RequestContext context, Object formObject, Errors errors) throws Exception { 926 Assert.notNull(validator, "The validator must not be null when attempting validation -- programmer error"); 927 String validatorMethodName = context.getAttributes().getString(VALIDATOR_METHOD_ATTRIBUTE); 928 if (StringUtils.hasText(validatorMethodName)) { 929 if (logger.isDebugEnabled()) { 930 logger.debug("Invoking validation method '" + validatorMethodName + "' on validator " + validator); 931 } 932 invokeValidatorMethod(validatorMethodName, formObject, errors); 933 } 934 else { 935 if (logger.isDebugEnabled()) { 936 logger.debug("Invoking validator " + validator); 937 } 938 getValidator().validate(formObject, errors); 939 } 940 if (logger.isDebugEnabled()) { 941 logger.debug("Validation completed for form object"); 942 logger.debug("There are [" + errors.getErrorCount() + "] errors, details: " + errors.getAllErrors()); 943 } 944 } 945 946 950 protected DispatchMethodInvoker getValidateMethodInvoker() { 951 return validateMethodInvoker; 952 } 953 954 960 protected FormObjectAccessor getFormObjectAccessor(RequestContext context) { 961 return new FormObjectAccessor(context); 962 } 963 964 966 983 protected Object createFormObject(RequestContext context) throws Exception { 984 if (formObjectClass == null) { 985 throw new IllegalStateException ("Cannot create form object without formObjectClass property being set -- " 986 + "either set formObjectClass or override createFormObject"); 987 } 988 if (logger.isDebugEnabled()) { 989 logger.debug("Creating new instance of form object class [" + formObjectClass + "]"); 990 } 991 return formObjectClass.newInstance(); 992 } 993 994 1011 protected void initBinder(RequestContext context, DataBinder binder) { 1012 } 1013 1014 1032 protected void registerPropertyEditors(RequestContext context, PropertyEditorRegistry registry) { 1033 registerPropertyEditors(registry); 1034 } 1035 1036 1051 protected void registerPropertyEditors(PropertyEditorRegistry registry) { 1052 if (propertyEditorRegistrar != null) { 1053 if (logger.isDebugEnabled()) { 1054 logger.debug("Registering custom property editors using configured registrar"); 1055 } 1056 propertyEditorRegistrar.registerCustomEditors(registry); 1057 } 1058 else { 1059 if (logger.isDebugEnabled()) { 1060 logger.debug("No property editor registrar set, no custom editors to register"); 1061 } 1062 } 1063 } 1064 1065 1072 protected boolean validationEnabled(RequestContext context) { 1073 return true; 1074 } 1075} | Popular Tags |