user1882812
user1882812

Reputation: 946

Validation with an EJB service call, possible with JSF validator or JSR303 bean validation?

i want to validate my username using a select. if the username already is present in the database, validation fails.

i found some annotations for primefaces like this for example:

@Size(min=2,max=5)  
private String name; 

i didnt found a annotation solution for something like this:

try {
    dao.findUserByUserName(userName);

    message = new FacesMessage ("Invalid username!", "Username Validation Error");
    message.setDetail("Username already exists!");
    message.setSeverity(FacesMessage.SEVERITY_ERROR);

    throw new ValidatorException(message);
} catch (NoSuchUserException e) {}

Do i have to use a custom validator or are there annotations for it?

Upvotes: 1

Views: 2303

Answers (1)

BalusC
BalusC

Reputation: 1109532

Do i have to use a custom validator or are there annotations for it?

It can be done both ways.

In case of a custom JSF validator, you only need to understand that an EJB cannot be injected in a @FacesValidator. You've basically 3 options:

  1. manually obtain it from JNDI
  2. or, make it a @ManagedBean instead of @FacesValidator
  3. or, install OmniFaces which adds transparent support for @EJB and @Inject in @FacesValidator

Ultimately, you can end up like this, assuming that you went for the @ManagedBean approach:

@ManagedBean
@RequestScoped
public class UsernameValidator implements Validator {

    @EJB
    private UserService service;

    public void validate(FacesContext context, UIComponent component, Object submittedValue) throws ValidatorException {
        if (submittedValue == null) {
            return; // Let required="true" handle.
        }

        String username = (String) submittedValue;

        if (service.exist(username) {
            throw new ValidatorException(new FacesMessage("Username already in use, choose another"));
        }
    }

}

Which is used as follows:

<h:inputText ... validator="#{usernameValidator}" />

In case of JSR303 bean validation, you'd need to create a custom @Constraint annotation along with a custom ConstraintValidator. You need to make sure that you're using at least CDI 1.1, otherwise you can't inject an EJB in a ConstraintValidator and you'd need to resort to manually grabbing it from JNDI in initialize() method. You can't solve it by making it a managed bean and even OmniFaces hasn't any magic for this.

E.g.

@Constraint(validatedBy = UsernameValidator.class)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Username {
    String message() default "Username already in use, choose another";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

with

public class UsernameValidator implements ConstraintValidator<Username, String> {

    @EJB
    private UserService service;

    @Override
    public void initialize(Username constraintAnnotation) {
        // If not on CDI 1.1 yet, then you need to manually grab EJB from JNDI here.
    }

    Override
    public boolean isValid(String username, ConstraintValidatorContext context) {
        return !service.exist(username);
    }

}

and in model

@Username
private String username;

Upvotes: 6

Related Questions