NoSuchMethodException - Problems using a RegexpValidator in an extended password dialog

Jmix version: 1.2.1
Jmix Studio plugin version: 1.2.1-213
IntelliJ IDEA 2021.3.2 CE - Build #IC-213.5744.223, built on November 27, 2021Runtime version: 11.0.13+7-b1751.19 x86_64
Operating System: macOS 12.3 (21E230)
File System: Case-Sensitive Journaled HFS+ (APFS)
Datebase: PostgreSQL 13

Hello Everyone

For your information, I have several problems regarding the RegexpValidator.

In CUBA’s web-app.properties it was possible to assign a regular expression to the cuba.passwordPolicyRegExp property but I have not found a Jmix equivalent, so I am trying to code a validator.

When I use the following code in an onAfterShow method…

RegexpValidator regexpValidator = applicationContext.getBean(RegexpValidator.class);

as similarly described here in the documentation…

https://docs.jmix.io/jmix/ui/vcl/miscellaneous/validator.html#regexp-validator

I receive this exception…

java.lang.NoSuchMethodException: io.jmix.ui.component.validation.RegexpValidator.<init>()

at java.base/java.lang.Class.getConstructor0(Class.java:3349)
	at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:353)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:233)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1282)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1243)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:494)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
	at com.company.nf.screen.extchangepassworddialog.ExtChangePasswordDialog.onAfterShow1(ExtChangePasswordDialog.java:41)
	at io.jmix.core.common.event.EventHub.publish(EventHub.java:170)
	at io.jmix.ui.screen.Screen.fireEvent(Screen.java:124)
	at io.jmix.ui.screen.UiControllerUtils.fireEvent(UiControllerUtils.java:58)
	at io.jmix.ui.sys.ScreensImpl.fireScreenAfterShowEvent(ScreensImpl.java:1369)
	at io.jmix.ui.sys.ScreensImpl.show(ScreensImpl.java:390)
	at io.jmix.ui.screen.Screen.show(Screen.java:306)…..

Furthermore, since I have extended the standard ChangePasswordDialog to my ExtChangePasswordDialog, I do not have any access to the ExtChangePasswordDialog’s passwordField, so I cannot setup the RegEx in my screen XML or use the

passwordField.addValidator(regexpValidator)

method as described in the documentation. I tried adding the following code with Studio’s “Generate Handler” function to my ExtChangePasswordDialog controller but it is never called:

@Install(to = "passwordField", subject = "validator")
private void passwordFieldValidator(String value) {
RegexpValidator regexpValidator = new RegexpValidator("((?=.*\\\\d)(?=.*[+$#*%&()=<>])(?=.*\\\\p{javaLowerCase})(?=.*\\\\p{javaUpperCase}).{10,20})", "ERROR: Invalid password format");
regexpValidator.accept(value);
}
  1. Is the exception above a bug or is there something missing in the documentation? Or I am doing something wrong?

  2. Why is the passwordFieldValidator() handler not called?

  3. Can you please explain in detail how I can install a RegexpValidator into my ExtChangePasswordDialog controller if the above method is incorrect?

Many thanks in advance.

Best regards
Chris

Hello!

PasswordField in the ChangePasswordDialog has a protected access modifier. So you can use it in the inheritors.

RegexpValidator is a Spring prototype bean and it does not have a default constructor. When you call getBean() you also need to pass arguments:

@Subscribe
public void onInit(InitEvent event) {
    getApplicationContext().getBean(RegexpValidator.class, "[a-zA-Z]*", "Error message"); // or
    getApplicationContext().getBean(RegexpValidator.class, "[a-zA-Z]*");
}

The validator is stored in the field and should be invoked by field.validate(). StandardEditor uses this method to validate values before saving an entity, but ChangePasswordDialog is a simple screen and you should call it manually.

You can override validatePassword() method and provide additional validation when user clicks on OK button:

@UiController("ext_ChangePasswordDialog")
@UiDescriptor("ext-change-password-dialog.xml")
public class ExtChangePasswordDialog extends ChangePasswordDialog {
    
    protected RegexpValidator regexpValidator;
    
    @Subscribe
    public void onInit(InitEvent event) {
        regexpValidator = getApplicationContext().getBean(RegexpValidator.class, "[a-zA-Z]*");
    }

    @Override
    protected ValidationErrors validatePassword(PasswordField passwordField, PasswordField confirmPasswordField, PasswordField currentPasswordField) {
        ValidationErrors errors = super.validatePassword(passwordField, confirmPasswordField, currentPasswordField);

        try {
            regexpValidator.accept(getPassword());
        } catch (ValidationException e) {
            if (errors.isEmpty()) {
                return ValidationErrors.of(e.getDetailsMessage());
            }
            errors.add(e.getDetailsMessage());
        }
        
        return errors;
    }

@pinyazhin

Hello Roman

Thank you very much for the detailed explanation, the examples and your efforts. It’s working now as expected.

Best regards
Chris