# Spring Security and form validators => NullPointer

65 4

Hello!

I have a form validator which works fine in almost every situation except if two clients are trying to edit the same object.

The first user (U1) opens an object to edit it, the second user (U2) opens the same object. U1 edits and saves the object (I'm using JPA with Hibernate) and the orm_version is bumped. The U2 tries to save his changes, but ObjectOptimisticLockingFailureException occurs - I catch those exceptions and I show the user an option to reload the data and see the changes. Then, if U2 reloads the data and make some changes and tries to save them, a NullPointer occurs. The properties, that should be in the ValidationContext property are missing. I suppose the problem is connected with Spring Security, which does not allow to copy/reload the add-edit object to its copy at the form.

I've created a dummy command by which the functionality of reloading is tested and it does not mess up the validator, so my other assumption is that the ObjectOptimisticLockingFailureException does something unexpected.

The validator is defined this way in the zul file: form="@id('fx') @load(vm.addEditObj) @save(vm.addEditObj, before='save') @validator(vm.validator)"

And here is the exception: java.lang.NullPointerException at bg.mycomphere.myprojhere.web.validators.BaseValidator.validateName(BaseValidator.java:17) at bg.mycomphere.myprojhere.web.vm.drivers.AddEditDriverVM$2.validate(AddEditDriverVM.java:81) at org.zkoss.bind.impl.SaveFormBindingImpl.validate(SaveFormBindingImpl.java:216) at org.zkoss.bind.impl.ValidationHelper.validateSaveFormBinding(ValidationHelper.java:317) at org.zkoss.bind.impl.ValidationHelper.validateSaveFormBefore(ValidationHelper.java:151) at org.zkoss.bind.impl.ValidationHelper.validateSaveBefore(ValidationHelper.java:127) at org.zkoss.bind.impl.BinderImpl.doValidate(BinderImpl.java:1998) at org.zkoss.bind.impl.BinderImpl.doCommand(BinderImpl.java:1766) at org.zkoss.bind.impl.BinderImpl.access$1300(BinderImpl.java:136) at org.zkoss.bind.impl.BinderImpl$CommandEventListener.onEvent0(BinderImpl.java:1625) at org.zkoss.bind.impl.BinderImpl$CommandEventListener.onEvent(BinderImpl.java:1578) at org.zkoss.zk.ui.AbstractComponent.onEvent(AbstractComponent.java:3177) at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3147) at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3089) at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138) at org.zkoss.zk.ui.impl.UiEngineImpl.processEvent(UiEngineImpl.java:1846) at org.zkoss.zk.ui.impl.UiEngineImpl.process(UiEngineImpl.java:1618) at org.zkoss.zk.ui.impl.UiEngineImpl.execUpdate(UiEngineImpl.java:1321) at org.zkoss.zk.au.http.DHtmlUpdateServlet.process(DHtmlUpdateServlet.java:611) at org.zkoss.zk.au.http.DHtmlUpdateServlet.doGet(DHtmlUpdateServlet.java:487) at org.zkoss.zk.au.http.DHtmlUpdateServlet.doPost(DHtmlUpdateServlet.java:495) at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:158) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:491) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:764) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1388) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)

I'll be glad if you have any ideas about the problem or hints what I can try to fix this behavior - some modification of the spring security configuration or other.

Thanks!

delete retag edit

Sort by » oldest newest most voted

cor3000
5364 2 7

Right now I can't imagine a reason why spring security should be involved in this case (However it would be interesting how things are connected). I'd try disabling it and check again. (Could be a quick way to remove one dimension of complexity in your case).

Next, your stack trace doesn't indicate what you are doing in your code, so I can't comment on that. Ideally can you share a runnable version reproducing the issue?

bg.mycomphere.myprojhere.web.validators.BaseValidator.validateName(BaseValidator.java:17) at
org.zkoss.bind.impl.SaveFormBindingImpl.validate(SaveFormBindingImpl.java:216)
...


You also mentioned you are "reloading the data". How is that done? Do you also reload the form proxy object?

65 4

Thank you, Robert! I found a solution/workaround on Monday, even tho I cant explain why this happens (but I couldn't post the solution due to 48h limit).

For some reason the second time the add-edit object is a proxy of the main object. We were getting the values from the context like this: ctx.getProperties(ctx.getProperty().getBase());

So when the object is stored as a proxy, the .getBase(), which returns a non-proxy version, does not find anything so I changed the code to get the values like this:

public static <t> T getValue(ValidationContext ctx, String key, Class<t> type) { Property[] props = ctx.getProperties(key); if (props == null || props.length == 0 || props[0] == null) return null;

return type.cast(props[0].getValue());


}

The reloading is simple fetching of the last state from the DB and sets the new values in the same object instance, then notifies all properties in this bean (add-edit object). The form is not reloaded, just notified.

Above I mentioned I tested this behavior with a command which does exactly the same, with only difference that the event is not triggered by an exception, but pressing a button and the object was instance of the class, not a proxy.

Anyway, it works fine now, but will be interesting why such behavior is observed in case of exception and not in case of a command / event.

thanks for getting back, I increased your Karma, so you have more options in the forum.

( 2019-06-13 10:21:33 +0800 )edit

It's still hard to picture the steps in my mind, without actually stepping through the code. So for now it remains a mystery ... ;) If the problem returns, and you have some simple, shareable and runnable code example I can have a look.

( 2019-06-13 10:25:29 +0800 )edit
[hide preview]