[
https://issues.apache.org/jira/browse/ISIS-712?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dan Haywood resolved ISIS-712.
------------------------------
Resolution: Fixed
> Inconsistency in domain logic for validation of optional strings causes
> Wicket viewer to trip up.
> -------------------------------------------------------------------------------------------------
>
> Key: ISIS-712
> URL: https://issues.apache.org/jira/browse/ISIS-712
> Project: Isis
> Issue Type: Bug
> Components: Core, Viewer: Wicket
> Affects Versions: viewer-wicket-1.3.1, core-1.3.0
> Reporter: Dan Haywood
> Assignee: Dan Haywood
> Priority: Critical
> Fix For: viewer-wicket-1.4.0, core-1.4.0
>
>
> First, to explain the issue. We have the following code:
> private String turnoverRentRule;
> @javax.jdo.annotations.Column(allowsNull = "true", length = 254)
> @Optional
> public String getTurnoverRentRule() { ... }
> public void setTurnoverRentRule(final String turnoverRentRule) { ... }
> public String validateTurnoverRentRule(final String rule) {
> if (rule == null || rule.trim().length() == 0) {
> return "Rule cannot be empty";
> }
> return rule.equals("INVALID")? "invalid rule!": null;
> }
> This code is logically inconsistent: the validateXxx() method suggests that
> the turnoverRentRule is required, yet the @Column(allowsNull="true") (and
> also the @Optional, though that is redundant) suggest the opposite.
> In the Wicket viewer, what I'm seeing when the field is left empty, is that
> Wicket's form#validate phase skips over and does not call the validate. This
> is because the validator that is installed by StringPanel's TextField
> implements IValidator but not INullAcceptingValidator, and Wicket does not
> check nulls on simple IValidators.
> I think the above is correct and shouldn't change.
> However, in the Wicket viewer, I then also go on in the form#submit phase to
> run the validation again, this time using Isis' infrastructure (see the stack
> trace below). This (redundantly) validates each property again, but (not
> redundantly) also performs any object-level checking, as per the any
> validate() method on the object.
> The above is also ok.
> Here's where the issues are:
> 1. with the above faulty domain object code, what we see is that the
> validateXxx() method does get called (by Isis) in the submit phase, even
> though it wasn't called (by Wicket) in the validate phase. This results in
> the "Rule cannot be empty" message being returned. However, the handling in
> EntityPropertiesForm#okButton#onSubmit that then detects this change
> accidentally switches the form to view mode. It should stay in edit mode.
> 2. the Isis PropertyValidateFacet ought to honour the @Optional semantic; if
> the proposed value is null, and the facet holder is not mandatory, then the
> validation should be skipped.
> This ticket is to fix both of these issues.
> Thread [1934620956@qtp-1127736517-7] (Suspended (breakpoint at line 194 in
> ToDoItem))
> ToDoItem.validateTurnoverRentRule() line: 194
> GeneratedMethodAccessor127.invoke(Object, Object[]) line: not available
> DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
> Method.invoke(Object, Object...) line: 606
> uV.invoke(Method, Object, Object[]) line: 1078
> Method.invoke(Object, Object...) line: 592
> MethodExtensions.invoke(Method, Object, Object[]) line: 50
> AdapterInvokeUtils.invoke(Method, ObjectAdapter, Object) line: 48
> AdapterInvokeUtils.invoke(Method, ObjectAdapter, ObjectAdapter) line:
> 52
> PropertyValidateFacetViaMethod.invalidReason(ObjectAdapter,
> ObjectAdapter) line: 61
>
> PropertyValidateFacetViaMethod(PropertyValidateFacetAbstract).invalidates(ValidityContext<ValidityEvent>)
> line: 55
> InteractionUtils.isValidResult(FacetHolder, ValidityContext<?>) line:
> 69
> OneToOneAssociationImpl.isAssociationValidResult(ObjectAdapter,
> ObjectAdapter) line: 110
> OneToOneAssociationImpl.isAssociationValid(ObjectAdapter,
> ObjectAdapter) line: 105
> ObjectValidPropertiesFacetImpl.invalidReason(ObjectValidityContext)
> line: 58
>
> ObjectValidPropertiesFacetImpl(ObjectValidPropertiesFacetAbstract).invalidates(ValidityContext<ValidityEvent>)
> line: 45
> InteractionUtils.isValidResult(FacetHolder, ValidityContext<?>) line:
> 69
>
> ObjectSpecificationDefault(ObjectSpecificationAbstract).isValidResult(ObjectAdapter)
> line: 1050
>
> ObjectSpecificationDefault(ObjectSpecificationAbstract).isValid(ObjectAdapter)
> line: 1040
> EntityModel.getReasonInvalidIfAny() line: 572
> EntityPropertiesForm$2.onSubmit() line: 377
> EntityPropertiesForm(Form).delegateSubmit(IFormSubmitter) line: 1253
> EntityPropertiesForm(Form).process(IFormSubmitter) line: 925
> EntityPropertiesForm(FormAbstract).process(IFormSubmitter) line: 118
> EntityPropertiesForm(Form).onFormSubmitted(IFormSubmitter) line: 771
> EntityPropertiesForm(Form).onFormSubmitted() line: 704
> GeneratedMethodAccessor163.invoke(Object, Object[]) line: not available
> DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
> Method.invoke(Object, Object...) line: 606
> RequestListenerInterface.internalInvoke(Component, Object) line: 258
> RequestListenerInterface.invoke(IRequestableComponent) line: 216
> ListenerInterfaceRequestHandler.invokeListener() line: 240
> ListenerInterfaceRequestHandler.respond(IRequestCycle) line: 226
> RequestCycle$HandlerExecutor.respond(IRequestHandler) line: 861
>
> RequestCycle$HandlerExecutor(RequestHandlerStack).execute(IRequestHandler)
> line: 64
> RequestCycle.execute(IRequestHandler) line: 261
> RequestCycle.processRequest() line: 218
> RequestCycle.processRequestAndDetach() line: 289
> WicketFilter.processRequestCycle(RequestCycle, WebResponse,
> HttpServletRequest, HttpServletResponse, FilterChain) line: 259
> WicketFilter.processRequest(ServletRequest, ServletResponse,
> FilterChain) line: 201
> WicketFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> line: 282
> ServletHandler$CachedChain.doFilter(ServletRequest, ServletResponse)
> line: 1212
> ShiroFilter(AbstractShiroFilter).executeChain(ServletRequest,
> ServletResponse, FilterChain) line: 449
> AbstractShiroFilter$1.call() line: 365
> SubjectCallable.doCall(Callable<V>) line: 90
> SubjectCallable.call() line: 83
> WebDelegatingSubject(DelegatingSubject).execute(Callable<V>) line: 383
> ShiroFilter(AbstractShiroFilter).doFilterInternal(ServletRequest,
> ServletResponse, FilterChain) line: 362
> ShiroFilter(OncePerRequestFilter).doFilter(ServletRequest,
> ServletResponse, FilterChain) line: 125
> ServletHandler$CachedChain.doFilter(ServletRequest, ServletResponse)
> line: 1212
> ServletHandler.handle(String, HttpServletRequest, HttpServletResponse,
> int) line: 399
> SecurityHandler.handle(String, HttpServletRequest, HttpServletResponse,
> int) line: 216
> SessionHandler.handle(String, HttpServletRequest, HttpServletResponse,
> int) line: 182
> WebAppContext(ContextHandler).__handle(String, HttpServletRequest,
> HttpServletResponse, int) line: 766
> WebAppContext(ContextHandler).handle(String, HttpServletRequest,
> HttpServletResponse, int) line: not available
> WebAppContext.handle(String, HttpServletRequest, HttpServletResponse,
> int) line: 450
> Server(HandlerWrapper).handle(String, HttpServletRequest,
> HttpServletResponse, int) line: 152
> Server.handle(HttpConnection) line: 326
> HttpConnection.handleRequest() line: 542
> HttpConnection$RequestHandler.content(Buffer) line: 945
> HttpParser.parseNext() line: 756
> HttpParser.parseAvailable() line: 212
> HttpConnection.handle() line: 404
> SocketConnector$Connection.run() line: 228
> QueuedThreadPool$PoolThread.run() line: 582
--
This message was sent by Atlassian JIRA
(v6.1.5#6160)