How to validate empty and not repeated jsf [duplicate] - validation

I have Bean validation working nicely in my application. Now I want to check that a new user does not choose a username that has already been chosen.
In the actionlistener I have the code that checks the database but how do I force the user to be sent back to the page they were on if they choose an already existing username?

Introduction
You can do it, but JSF ajax/action/listener methods are semantically the wrong place to do validation. You actually don't want to get that far in JSF lifecycle if you've wrong input values in the form. You want the JSF lifecycle to stop after JSF validations phase.
You want to use a JSR303 Bean Validation annotation (#NotNull and friends) and/or constraint validator, or use a JSF Validator (required="true", <f:validateXxx>, etc) for that instead. It will be properly invoked during JSF validations phase. This way, when validation fails, the model values aren't updated and the business action isn't invoked and you stay in the same page/view.
As there isn't a standard Bean Validation annotation or JSF Validator for the purpose of checking if a given input value is unique according the database, you'd need to homegrow a custom validator for that.
I'll for both ways show how to create a custom validator which checks the uniqueness of the username.
Custom JSR303 Bean Validation Annotation
First create a custom #Username constraint annotation:
#Constraint(validatedBy = UsernameValidator.class)
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public #interface Username {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
With this constraint validator (note: #EJB or #Inject inside a ConstraintValidator works only since CDI 1.1; so if you're still on CDI 1.0 then you'd need to manually grab it from JNDI):
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);
}
}
Finally use it as follows in model:
#Username
private String username;
Custom JSF Validator
An alternative is to use a custom JSF validator. Just implement the JSF Validator interface:
#ManagedBean
#RequestScoped
public class UsernameValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object submittedAndConvertedValue) throws ValidatorException {
String username = (String) submittedAndConvertedValue;
if (username == null || username.isEmpty()) {
return; // Let required="true" or #NotNull handle it.
}
if (userService.exist(username)) {
throw new ValidatorException(new FacesMessage("Username already in use, choose another"));
}
}
}
Finally use it as follows in view:
<h:inputText id="username" ... validator="#{usernameValidator}" />
<h:message for="username" />
Note that you'd normally use a #FacesValidator annotation on the Validator class, but until the upcoming JSF 2.3, it doesn't support #EJB or #Inject. See also How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired.

Yes you can. You can do validation in action listener method, add faces messages if your custom validation failed, then call FacesContext.validationFailed() just before return.
The only problem with this solution is, it happens after the JSF validation and bean validation. I.e., it is after the validation phase. If you have multiple action listeners, say listener1 and listener2: if your custom validation in listener1 failed, it will continue to execute listener2. But after all, you'll get validationFailed in AJAX response.

It's better to use action method instead of actionListener for this purpose. Then you can return null (reloads page that triggered the action) from this method if the username exists. Here's an example:
in the facelet:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
in the bean:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}

If you want to provide feedback to end-user:
xhtml:
<p:commandButton value="Go" process="#this" action="#{myBean.checkEntity()}" oncomplete="if(args.validationFailed){PF('widgetOldInfoNotice').show();}"/>
<p:confirmDialog id="dialogOldInfoNotice" header="NOTICE" severity="alert" widgetVar="widgetOldInfoNotice">
-- feedback message--
<p:button value="Ok" onclick="PF('widgetOldInfoNotice').hide();"/>
</p:confirmDialog>
bean:
public String checkEntity() {
if (!dao.whateverActionToValidateEntity(selectedEntity)) {
FacesContext context = FacesContext.getCurrentInstance();
context.validationFailed();
return "";
}
return "myPage.xhtml";
}

You can define a navigation case in the faces-config.xml file. This will allow you to redirect the user to a given page depending on the return value of the bean.
In the example below a suer is redirected to one of two pages depending on the return value of "myMethod()".
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>true</from-outcome>
<to-view-id>/correct.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>false</from-outcome>
<to-view-id>/error.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

Related

Understanding JSF-validators. Do I really need one in this case? [duplicate]

I have Bean validation working nicely in my application. Now I want to check that a new user does not choose a username that has already been chosen.
In the actionlistener I have the code that checks the database but how do I force the user to be sent back to the page they were on if they choose an already existing username?
Introduction
You can do it, but JSF ajax/action/listener methods are semantically the wrong place to do validation. You actually don't want to get that far in JSF lifecycle if you've wrong input values in the form. You want the JSF lifecycle to stop after JSF validations phase.
You want to use a JSR303 Bean Validation annotation (#NotNull and friends) and/or constraint validator, or use a JSF Validator (required="true", <f:validateXxx>, etc) for that instead. It will be properly invoked during JSF validations phase. This way, when validation fails, the model values aren't updated and the business action isn't invoked and you stay in the same page/view.
As there isn't a standard Bean Validation annotation or JSF Validator for the purpose of checking if a given input value is unique according the database, you'd need to homegrow a custom validator for that.
I'll for both ways show how to create a custom validator which checks the uniqueness of the username.
Custom JSR303 Bean Validation Annotation
First create a custom #Username constraint annotation:
#Constraint(validatedBy = UsernameValidator.class)
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public #interface Username {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
With this constraint validator (note: #EJB or #Inject inside a ConstraintValidator works only since CDI 1.1; so if you're still on CDI 1.0 then you'd need to manually grab it from JNDI):
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);
}
}
Finally use it as follows in model:
#Username
private String username;
Custom JSF Validator
An alternative is to use a custom JSF validator. Just implement the JSF Validator interface:
#ManagedBean
#RequestScoped
public class UsernameValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object submittedAndConvertedValue) throws ValidatorException {
String username = (String) submittedAndConvertedValue;
if (username == null || username.isEmpty()) {
return; // Let required="true" or #NotNull handle it.
}
if (userService.exist(username)) {
throw new ValidatorException(new FacesMessage("Username already in use, choose another"));
}
}
}
Finally use it as follows in view:
<h:inputText id="username" ... validator="#{usernameValidator}" />
<h:message for="username" />
Note that you'd normally use a #FacesValidator annotation on the Validator class, but until the upcoming JSF 2.3, it doesn't support #EJB or #Inject. See also How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired.
Yes you can. You can do validation in action listener method, add faces messages if your custom validation failed, then call FacesContext.validationFailed() just before return.
The only problem with this solution is, it happens after the JSF validation and bean validation. I.e., it is after the validation phase. If you have multiple action listeners, say listener1 and listener2: if your custom validation in listener1 failed, it will continue to execute listener2. But after all, you'll get validationFailed in AJAX response.
It's better to use action method instead of actionListener for this purpose. Then you can return null (reloads page that triggered the action) from this method if the username exists. Here's an example:
in the facelet:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
in the bean:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}
If you want to provide feedback to end-user:
xhtml:
<p:commandButton value="Go" process="#this" action="#{myBean.checkEntity()}" oncomplete="if(args.validationFailed){PF('widgetOldInfoNotice').show();}"/>
<p:confirmDialog id="dialogOldInfoNotice" header="NOTICE" severity="alert" widgetVar="widgetOldInfoNotice">
-- feedback message--
<p:button value="Ok" onclick="PF('widgetOldInfoNotice').hide();"/>
</p:confirmDialog>
bean:
public String checkEntity() {
if (!dao.whateverActionToValidateEntity(selectedEntity)) {
FacesContext context = FacesContext.getCurrentInstance();
context.validationFailed();
return "";
}
return "myPage.xhtml";
}
You can define a navigation case in the faces-config.xml file. This will allow you to redirect the user to a given page depending on the return value of the bean.
In the example below a suer is redirected to one of two pages depending on the return value of "myMethod()".
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>true</from-outcome>
<to-view-id>/correct.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>false</from-outcome>
<to-view-id>/error.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

How to validate multiple fields in JSF?

I'm going through validation in JSF and I see lots of examples of very basic logic. Frankly, I put them in the same category where the HelloWorld examples go. I can't imagine placing error messages in xhtml files, using a separate validation method for each validated field or employing bean validation.
What I want to do, is have a single method on the backing bean that will execute validation for each field, logging error messages driven by keys in i18n property files.
Can this be done? If so, how do we register that method as validating method, how do we obtain submitted field values for evaluation, and how do we register error messages?
<h:inputText id="username" value="#{bean.username}" label="UserName" binding="#{bean.component}"/>
<h:message for="username" />
<h:commandButton value="Submit" action="#{bean.actionMethod}" />
In your bean class,
private UIComponent component;
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
public String actionMethod() {
if (!validate()) {
return null;
}
// do your action method logic
}
private boolean validate() {
FacesContext context = FacesContext.getCurrentInstance();
//do validation for your fields and add to faces messages
FacesMessage msg = new FacesMessage(severity, summary, detail);
context.addMessage(component.getClientId(), msg);
// do for other fields
return status;
}
Refer this to get component client id
How to add a message to a specific component from JSF backing bean

How to apply JSF Validator after annotations constraints?

My problem is more about performance than functionality.
I am dealing with an email field for a sign up form.
For its validation, I use annotations in the User entity for the email format, and an EmailExistValidator to check in the database if this email is already used.
#Entity
#Table(name = "Users")
public class User {
#Column(name = "email")
#NotNull(message = "Email address required")
#Pattern(regexp = "([^.#]+)(\\.[^.#]+)*#([^.#]+\\.)+([^.#]+)", message = "Invalid email address")
private String email;
// ... (other fields and getters/setters here)
}
The validator:
#FacesValidator(value = "emailExistValidator")
public class EmailExistValidator implements Validator {
private static final String EMAIL_ALREADY_EXISTS = "This email address is already used.";
#EJB
private UserDao userDao;
#Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
String email = (String) value;
try {
if (email != null && userDao.findByEmail(email) != null) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
EMAIL_ALREADY_EXISTS, null));
}
} catch (DaoException e) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),
null);
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.addMessage(component.getClientId(facesContext), message);
}
}
}
According to my tests, if an email does not verify the regexp pattern, the validator is still applied. I don't like the idea of performing a database query to check for email existence while I already know this email is not even valid. It could really slow down the performance (even more when using ajax on blur events in the form).
I have some guesses but I couldn't find clear answers, that's why I ask a few questions here:
Is this a bad thing to mix a validator with annotation constraints?
During the validation, is every constraint checked even if one of them does not pass? (If yes, then are all the messages (for each constraint) added to the FacesContext for the component, or just the first message?)
If yes to question 2, is there any way to force the validation to stop as soon as one constraint is not verified?
If no to question 2 or yes to question 3, is the order of application of the constraints/validator specified somewhere? Is this order customizable?
If this matters, I'm using PrimeFaces for my Facelets components.
I know I could put everything in the validator and stop whenever, but this validator is to be used only for the Sign Up form. When used for signing in, I would only check the entity's annotation constraints, not the "already exists" part. In addition, annotations and single constraint validators are to my mind more readable than the content of a multi-constraint validator.
JSF validation runs by design before JSR303 bean validation. So there's technically no way to skip JSF validation when bean validation fails. If they would run the other way round, then you would theoretically simply have checked UIInput#isValid() in the JSF validator:
UIInput input = (UIInput) component;
if (!input.isValid()) {
return;
}
There's unfortunately no API-provided way to control the JSF-JSR303 validation order. Your best bet is to turn the JSF validator into a true JSR303 bean validator and assign it to a different group which you declare to run after the default group. In JSR303, constraints are validated on a per-group basis. If one validation group fails, then any subsequent validation groups are not executed.
First create a custom JSR303 bean validation constraint annotation:
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UniqueEmailValidator.class)
#Documented
public #interface UniqueEmail {
String message() default "{invalid.unique.email}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Then create a custom JSR303 bean validation constraint validator:
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
#Override
public void initialize(UniqueEmail annotation) {
// Grab EJB here via JNDI?
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return userDao.findByEmail(value) == null;
}
}
Unfortunately, #EJB/#Inject in a ConstraintValidator isn't natively supported in Java EE 6 / JSR303 Bean Validation 1.0. It's only supported in Java EE 7 / JSR303 Bean Validation 1.1. They are however available via JNDI.
Then create a custom validation group:
public interface ExpensiveChecks {}
And finally use it on your entity whereby the group ordering is declared via #GroupSequence annotation (the default group is identified by current class):
#Entity
#Table(name = "Users")
#GroupSequence({ User.class, ExpensiveChecks.class })
public class User {
#Column(name = "email")
#NotNull(message = "Email address required")
#Pattern(regexp = "([^.#]+)(\\.[^.#]+)*#([^.#]+\\.)+([^.#]+)", message = "Invalid email address")
#UniqueEmail(groups = ExpensiveChecks.class, message = "This email address is already used")
private String email;
// ...
}

jsf converter loses injected property

I had this working before, but then I changed some things, and I can't get it to work again. I am trying to use my service tier to hit the database and get a correct object from my converter class, depending on what the user clicks. I inject the service property into my converter with spring. During debugging, I can see that the property gets sets properly. But then when I go to call getService, it is null.
#FacesConverter("PlaceConverter")
#SessionScoped
public class PlaceConverter implements Converter {
private SearchQueryService searchQueryService;
/**
* #return the searchQueryService
*/
public SearchQueryService getSearchQueryService() {
return searchQueryService;
}
/**
* #param searchQueryService the searchQueryService to set
*/
public void setSearchQueryService(SearchQueryService searchQueryService) {
this.searchQueryService = searchQueryService;
}
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String submittedValue) {
try {
Criteria criteria = new Criteria();
criteria.setId(Integer.parseInt(submittedValue));
return getSearchQueryService().findPlaces(criteria).get(0);
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
((Place) value).setCategory(" (" + ((Place) value).getCategory() + ")");
return String.valueOf(((Place) value).getPlaceId());
}
}
<bean id="placeConverterBean" class="com.ghghg.converter.PlaceConverter">
<property name="searchQueryService" ref="searchQueryServiceBean" />
</bean>
Dependency injection in a converter works only if the converter is declared as a managed bean by the dependency injection framework in question. E.g. JSF's own #ManagedBean, or CDI's #Named, or Spring's #Component. You should remove the #FacesConverter altogether and reference the converter instance in EL scope instead of referencing it by the converter ID.
Thus, so
<h:inputXxx converter="#{placeConverter}" />
or
<f:converter binding="#{placeConverter}" />
instead of
<h:inputXxx converter="PlaceConverter" />
or
<f:converter converterId="PlaceConverter" />
Your concrete problem suggests that you were referencing it by converter ID (thus, via #FacesConverter). This way you end up getting a converter instance without any injected dependencies.
See also:
How to inject Spring bean into JSF converter
As to the role of the converter itself, this is mandatory because HTML code is represented as one large string and HTTP request parameter values can only be represented as strings. Complex Java objects would otherwise be printed via Object#toString() like so com.example.Place#hashcode, making it unusable in the server side.
I found a better way, and probably more proper way to do get what I wanted. I was not completely sure how the converter works and how the value of the selected item gets passed back to the managed bean. I just declared a new Place object in my method, set the required values. Then I saw that it got passed to my managed bean
I got it to work like this in java EE with jsf 2.0. By making the converter a member of the backing bean. I instantiate this member using CDI but it should work the same with spring.
First the backing bean:
#ViewScoped
#ManagedBean
public class SomeView implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private SomeConverter converter;
public Converter getConverter() {
return converter;
}
}
And then this is the jsf xhtml:
<p:selectOneMenu id="someId" value="#{someView.value}" converter="#{someView.converter}">
<f:selectItems value="#{someView.values}" var="object" itemLabel="#{object.name}" />
</p:selectOneMenu>
Converter comes to play before updating your model bean. When user fill some input and this value is transferred to server first are updated your server side components and next conversion has happened. Converted values as saved in your bean (with method getAsObject) and before rendering the view values from beans are again converted to String because from user side everything is a string (then method getAsString is invoked).
In summary - Converter methods are the best place to change user input into your application logic, bean fields and in other way to convert your logic, bean fields into user friendly strings.
Due to your question and problem. You mean that SearchQueryService isn't available inside getAsObject method. Try to add an addnotation #Resource with proper name attribute and then it should be injected by your container.

Conditional validations in Spring

I have a form which contain some radio button and check-boxes. Whenever user selects/deselects a radio button or check-box few other input fields are enabled/disabled.
I want to add validation only for fields which are enabled when user submits the form. Fields which are disabled at the time of submission should not be considered for validation.
I don't want to add this in client side validation.Is there any better /easy way to implement conditional validation in Spring 3.0 instead of adding multiple if in validator ?
Thanks!
If you use JSR 303 Bean validation then you can use validation groups (groups) for this.
Assume you have this user input, containing two sections.
The two booleans indicating if the sections are enabled or disabled. (Of course you can use more useful annotations than #NotNull)
public class UserInput {
boolean sectionAEnabled;
boolean sectionBEnabled;
#NotNull(groups=SectionA.class)
String someSectionAInput;
#NotNull(groups=SectionA.class)
String someOtherSectionAInput;
#NotNull(groups=SectionB.class)
String someSectionBInput;
Getter and Setter
}
You need two Interfaces for the groups. They work only as marker.
public interface SectionA{}
public interface SectionB{}
Since Spring 3.1 you can use the Spring #Validated annotation (instead of #Validate) in your controller method to trigger the validation:
#RequestMapping...
public void controllerMethod(
#Validated({SectionGroupA.class}) UserInput userInput,
BindingResult binding, ...){...}
Before Spring 3.1 there was no way to specify the validation group that should been used for validation (because #Validated does not exist and #Validate does not have a group attribute), so you need to start the validation by hand written code: This an an example how to trigger the validation in dependence to witch section is enabled in Spring 3.0.
#RequestMapping...
public void controllerMethod(UserInput userInput,...){
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
List<Class<?>> groups = new ArrayList<Class<?>>();
groups.add(javax.validation.groups.Default.class); //Always validate default
if (userInput.isSectionAEnabled) {
groups.add(SectionA.class);
}
if (userInput.isSectionBEnabled) {
groups.add(SectionB.class);
}
Set<ConstraintViolation<UserInput>> validationResult =
validator.validate(userInput, groups.toArray(new Class[0]));
if(validationResult.isEmpty()) {
...
} else {
...
}
}
(BTW: For the Spring 3.0 solution, it is also possible to let Spring inject the validator:
#Inject javax.validation.Validator validator
<mvc:annotation-driven validator="validator"/>
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
)
When fields are disabled they are transferred to controller as null. I wanted to add a validation which will allow either null i.e disabled field or not blank field i.e. enabled but empty field.
So I created a custom annotation NotBlankOrNull which will allow null and non empty strings also it takes care of blank spaces.
Here is my Annotation
#Documented
#Constraint(validatedBy = { NotBlankOrNullValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface NotBlankOrNull {
String message() default "{org.hibernate.validator.constraints.NotBlankOrNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
Validator class
public class NotBlankOrNullValidator implements ConstraintValidator<NotBlankOrNull, String> {
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if ( s == null ) {
return true;
}
return s.trim().length() > 0;
}
#Override
public void initialize(NotBlankOrNull constraint) {
}
}
I have also updated more details on my site.

Resources