java.util.ConcurrentModificationException if binder withValidator update m

Reproduce step:

  1. Goto https://start.spring.io/ download example
  • Spring Boot: 2.3.4
  • Packaging: jar
  • Java: 11
  • Dependencies: Vaadin web
  • Vaadin: 14.4.1
  1. Update MainView.java
  • Ref attached file: ‘MainView.java’
  • My IDE eclipse & MacOS 10.15.7 & Java 11.0.5
  1. Error Message
  • Ref attached file: ‘error.log.txt’
  • Code snapshot:

Error at line “com.example.demo.MainView.lambda$0(MainView.java:42)”
And code occur is “firstName.setValue(“Leo-1002”)”

binder.forField(empNo).asRequired().withValidator((val, context) -> {
			System.out.println(String.format("old: %s, val: %s", binder.getBean().getEmpNo(), val));
			if (1002 == val) {
				firstName.setValue("Leo-1002");
				lastName.setValue("Tu-1002");
			}
			return ValidationResult.ok();
		}).bind("empNo");

18462102.java (2.84 KB)

2020-10-22 15:39:21.343  INFO 9541 --- [nio-8080-exec-1]
 c.v.f.s.DefaultDeploymentConfiguration   : 
Vaadin is running in DEBUG MODE.
When deploying application for production, remember to disable debug features. See more from https://vaadin.com/docs/
2020-10-22 15:39:21.384  INFO 9541 --- [nio-8080-exec-1]
 c.vaadin.flow.spring.SpringInstantiator  : The number of beans implementing 'I18NProvider' is 0. Cannot use Spring beans for I18N, falling back to the default behavior
old: 1001, val: 1001
old: 1001, val: 1002
old: 1001, val: 1002
old: 1001, val: 1002
old: 1001, val: 1002
2020-10-22 15:39:24.459 ERROR 9541 --- [nio-8080-exec-2]
 c.v.flow.server.DefaultErrorHandler      : 

java.util.ConcurrentModificationException: null
	at java.base/java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719) ~[na:na]

	at java.base/java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:741) ~[na:na]

	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[na:na]

	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]

	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]

	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]

	at com.vaadin.flow.data.binder.Binder.doWriteIfValid(Binder.java:1946) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder.handleFieldValueChange(Binder.java:1560) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.handleFieldValueChange(Binder.java:1230) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.lambda$new$f9b94f89$1(Binder.java:1068) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.lambda$addValueChangeListener$828eca10$1(AbstractFieldSupport.java:96) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:205) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentEventBus.fireEvent(ComponentEventBus.java:194) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.Component.fireEvent(Component.java:359) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentUtil.fireEvent(ComponentUtil.java:386) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.setValue(AbstractFieldSupport.java:207) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.setValue(AbstractFieldSupport.java:133) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.AbstractField.setValue(AbstractField.java:181) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.textfield.TextField.setValue(TextField.java:442) ~[vaadin-text-field-flow-2.3.1.jar:na]

	at com.example.demo.MainView.lambda$0(MainView.java:42) ~[classes/:na]

	at com.vaadin.flow.data.binder.Binder$BindingBuilderImpl.lambda$withValidator$7d13c2a2$1(Binder.java:898) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$ValidatorAsConverter.convertToModel(Binder.java:1375) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.converter.Converter$2.lambda$convertToModel$6b579330$1(Converter.java:165) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.SimpleResult.flatMap(SimpleResult.java:65) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.ValidationResultWrap.flatMap(ValidationResultWrap.java:65) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.converter.Converter$2.convertToModel(Converter.java:165) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.converter.Converter$2.convertToModel(Converter.java:163) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.doConversion(Binder.java:1140) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.doValidation(Binder.java:1157) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.validate(Binder.java:1100) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder.lambda$doWriteIfValid$3(Binder.java:1946) ~[flow-data-2.4.0.jar:2.4.0]

	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]

	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[na:na]

	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]

	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]

	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]

	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]

	at com.vaadin.flow.data.binder.Binder.doWriteIfValid(Binder.java:1946) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder.handleFieldValueChange(Binder.java:1560) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.handleFieldValueChange(Binder.java:1230) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.data.binder.Binder$BindingImpl.lambda$new$f9b94f89$1(Binder.java:1068) ~[flow-data-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.lambda$addValueChangeListener$828eca10$1(AbstractFieldSupport.java:96) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:205) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentEventBus.fireEvent(ComponentEventBus.java:194) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.Component.fireEvent(Component.java:359) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.ComponentUtil.fireEvent(ComponentUtil.java:386) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.setValue(AbstractFieldSupport.java:207) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.internal.AbstractFieldSupport.setModelValue(AbstractFieldSupport.java:167) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.AbstractField.setModelValue(AbstractField.java:225) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.AbstractSinglePropertyField.handlePropertyChange(AbstractSinglePropertyField.java:352) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.AbstractSinglePropertyField.access$200(AbstractSinglePropertyField.java:48) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.component.AbstractSinglePropertyField$1.propertyChange(AbstractSinglePropertyField.java:325) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.lambda$fireEvent$2(ElementPropertyMap.java:454) ~[flow-server-2.4.0.jar:2.4.0]

	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]

	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.fireEvent(ElementPropertyMap.java:454) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.access$100(ElementPropertyMap.java:48) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap$PutResult.run(ElementPropertyMap.java:166) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.communication.ServerRpcHandler.runMapSyncTask(ServerRpcHandler.java:395) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$0(ServerRpcHandler.java:389) ~[flow-server-2.4.0.jar:2.4.0]

	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]

	at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:389) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:317) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1545) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:247) ~[flow-server-2.4.0.jar:2.4.0]

	at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:111) ~[vaadin-spring-12.3.2.jar:na]

	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.38.jar:4.0.FR]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:352) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.38.jar:4.0.FR]

	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.38.jar:4.0.FR]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]

	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]

	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.38.jar:9.0.38]

	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

18462110.txt (31.2 KB)

It only happens when multiple components are updated,
no error if just update one.

  1. No error only update firstName
binder.forField(empNo).asRequired().withValidator((val, context) -> {
			System.out.println(String.format("old: %s, val: %s", binder.getBean().getEmpNo(), val));
			if (1002 == val) {
				firstName.setValue("Leo-1002");
//				lastName.setValue("Tu-1002");
			}
			return ValidationResult.ok();
		}).bind("empNo");
  1. No error only update lastName
binder.forField(empNo).asRequired().withValidator((val, context) -> {
			System.out.println(String.format("old: %s, val: %s", binder.getBean().getEmpNo(), val));
			if (1002 == val) {
//				firstName.setValue("Leo-1002");
				lastName.setValue("Tu-1002");
			}
			return ValidationResult.ok();
		}).bind("empNo");
  1. Error update both firstName and lastName
binder.forField(empNo).asRequired().withValidator((val, context) -> {
			System.out.println(String.format("old: %s, val: %s", binder.getBean().getEmpNo(), val));
			if (1002 == val) {
				firstName.setValue("Leo-1002");
				lastName.setValue("Tu-1002");
			}
			return ValidationResult.ok();
		}).bind("empNo");

Please refer to the attached file for complete project code.
18462146.zip (74.8 KB)

I’m not sure I understand what you’re doing, but updating fields inside a Validator sounds like the wrong approach. If you want to update the first name and last name when the employee number changes, you should use a value change listener in the empNo field.

  1. When user input “empNo” field, after all validators checked passed, the last step will query database by “empNo” id to find if existing and filter by SQL condition checking.
  2. All checking-rules are correct then update others field’s value that retrieved from database.
  3. If using change listener in the empNo field, that query database missing pre-check rules. (“addValueChangeListener” received value is not guaranteed all filtered through ValidationResult.ok)
  4. The Employees data can’t send to addValueChangeListener by event message.
 binder.forField(empNo).asRequired())
                .withValidator((val, context) -> { // (1) condition checking
				    ...
				})
				.withValidator((val, context) -> { // (2) condition checking
				    ...
				})
                .withValidator((val, context) -> { // (3) last step will query database
                     Employees emp = empService.findById(val).orElse(null); // (4) retrieved from database
                    if (emp == null) {
                        return ValidationResult.error(...);
                    } else {
                        firstName.setValue(emp.getFirstName(); // (5) update value that from database
                        lastName.setValue(emp.getLastName());
                        return ValidationResult.ok();
                    }
                }).bind("empNo");
  1. When user input “empNo” field, after all validators checked passed, the last step will query database by “empNo” id to find if existing and filter by SQL condition checking.
  2. All checking-rules are correct then update others field’s value that retrieved from database.
  3. If using change listener in the empNo field, that query database missing pre-check rules. (“addValueChangeListener” received value is not guaranteed all filtered through ValidationResult.ok)
  4. The Employees data can’t send to addValueChangeListener by event message.
 binder.forField(empNo).asRequired())
                .withValidator((val, context) -> { // (1) condition checking
				    ...
				})
				.withValidator((val, context) -> { // (2) condition checking
				    ...
				})
                .withValidator((val, context) -> { // (3) last step will query database
                     Employees emp = empService.findById(val).orElse(null); // (4) retrieved from database
                    if (emp == null) {
                        return ValidationResult.error(...);
                    } else {
                        firstName.setValue(emp.getFirstName(); // (5) update value that from database
                        lastName.setValue(emp.getLastName());
                        return ValidationResult.ok();
                    }
                }).bind("empNo");

Generally speaking, Validators should probably not have any side effects.

Set a validator on Binder itself (binder.withValidator() instead of binder.forField(field).withValidator()) if you want to add a validator that gets executed after individual field validators.

You can also set a value change listener to the Binder itself with binder.addValueChangeListener - check the API documentation on how it behaves (it will change on any field’s change, but not trigger if the validator in the changed field did not pass and you get the component from the event).

If you want react to validation status changes, you can do that with binder.setValidationStatusHandler(). It’s probably the most versatile option, with lots of info available in the BinderValidationStatus input object.

Note that you can also check for the validation status of the entire Binder with binder.isValid().

Thanks, I get it.
This is what I do:

  1. Implements ValidationResult carry with data
public class ValidationResultOkWithData implements ValidationResult {
        private final Object data;
        public ValidationResultOkWithData(Object data) { // <--- carry with data
            this.data = data;
        }
        @Override
        public String getErrorMessage() {
            return "";
        }
        public Optional<ErrorLevel> getErrorLevel() {
            return Optional.empty();
        }
        public <T> T getData() {
            return (T)data;
        }
    }
  1. withValidator Ok with data (Employees)
binder.forField(empNo).asRequired())
                .withValidator((val, context) -> { // (1) condition checking
				    ...
				})
				.withValidator((val, context) -> { // (2) condition checking
				    ...
				})
                .withValidator((val, context) -> { // (3) last step will query database
                    Employees emp = empService.findById(val).orElse(null); // (4) retrieved from database
                    if (emp == null) {
                        return ValidationResult.error(...);
                    } else {
                        return new ValidationResultOkWithData(emp); // <--- carry with data
                    }
                }).bind("empNo");
  1. It works for “binder.setValidationStatusHandler” to find ValidationResultOkWithData object
 binder.setValidationStatusHandler(evt -> {
            Optional<Employees> opt = fetchData(evt, fvs -> fvs.getField() == empNo);
            opt.ifPresent(emp -> {
                firstName.setValue(emp.getFirstName());
                lastName.setValue(emp.getLastName());
            });
        });
		...
		...
 public <T> Optional<T> fetchData(BinderValidationStatus<?> bvs, Predicate<BindingValidationStatus<?>> predicate) {
        AtomicReference<ValidationResultOkWithData> result = new AtomicReference<>();
        bvs.getFieldValidationStatuses().stream().filter(fvs -> !fvs.isError() && predicate.test(fvs)).forEach(fvs -> {
            fvs.getValidationResults().stream().filter(vr -> vr instanceof ValidationResultOkWithData && !vr.isError()) // <--- search data
                    .findFirst().ifPresent(vr -> result.set((ValidationResultOkWithData) vr));
        });
        return Optional.ofNullable((T) result.get() == null ? null : result.get().getData());
    }