How to bind custom jsf-validators to programmatically created HTML Elements? - validation

I am using JSF 2.0 and want to attach my custom RangeValidator to a HtmlInputText. In my backing bean, there is a snippet which creates the HTMLInputText. If I add a default validator, my code works like expected. If I add my custom RangeValidator in the same manner (by using FacesContext...) there is an error coming up, more precisely an 'unknown id' error. If I create an instance by using the new keyword, it seems that I am not able to use my accessor-methods, more precisely it seems that my HtmlInputText will use a freshly created RangeValidator-instance at runtime, so property values are minimum = 0 and maximum = 0, my validator is useless. How can I get my custom validator working? Thanks in advance.
// Snippet which creates HTML-Components
if ((integerField.getMaximalValue() != null) && (integerField.getMinimalValue() != null)) { // serverside validation required
// Default JSF Validator - works!
// final LongRangeValidator validator = (LongRangeValidator) FacesContext.getCurrentInstance().getApplication().createValidator(LongRangeValidator.VALIDATOR_ID);
// CustomValidator - ERROR: Unknown validator id;
final RangeValidator validator = (RangeValidator) FacesContext.getCurrentInstance().getApplication().createValidator("mypackage.validators.RangeValidator");
// construct custom validator instance - not working, seems to be a different instance!
// RangeValidator validator = new RangeValidator();
validator.setMaximum(integerField.getMaximalValue());
validator.setMinimum(integerField.getMinimalValue());
inputInteger.addValidator(validator);
}
// Validator
#FacesValidator("RangeValidator")
public class RangeValidator implements Validator {
private int minimum;
private int maximum;
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
//System.out.println("RangeValidator.validate: " + value);
int val = Integer.parseInt((String) value);
if ((val <= this.getMaximum()) && (val >= this.getMinimum())){
// ok
} else {
FacesMessage fm = new FacesMessage();
String message = "Das Feld " + uiComponent.getClientId() + " liefert Validierungsfehler!";
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
fm.setSummary(message);
fm.setDetail(message);
throw new ValidatorException(fm);
}
}
// Accessors
...
}

I found a solution for the problem: RangeValidator needs to implement the Serializable-interface as well and all will work like expected;

Related

Manually call Hibernate URL validator in spring-boot

I am using spring-boot 1.4.0 and hibernate-validator 5.2.0. I have a model which contains custom validator inside the custom validator i want to check whether the property value is a valid URL for that i need to call URLValidator in hibernate but no luck.Could anyone please guide me to resolve this issue
CustomValidator.java
#Component
public class BookValidator extends GenericValidator<Book, ConstraintValidatorContext> implements ConstraintValidator<ValidBooks, List<Book>> {
public BookValidator() {
addValidators();
}
private void addValidators() {
getValidators().add((book, context) -> {
boolean isValid = book.getUrl(); //here i want to check against Hibernate URL validator
if (!isValid) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate("Book URL should be valid!")
.addConstraintViolation();
}
return isValid;
});
}
#Override
public void initialize(ValidBooks constraintAnnotation) {
}
}
How can i check whether the URL is a valid one
boolean isValid = book.getUrl(); using hibernate URLValidator?
This works:
AnnotationDescriptor<URL> descriptor = new AnnotationDescriptor<URL>( URL.class );
URL url = AnnotationFactory.create(descriptor);
URLValidator urlValidator = new URLValidator();
urlValidator.initialize(url);
boolean isValid = urlValidator.isValid(book.getUrl(), context);

How to detect valueChange event in validator?

Mojarra 2.1
I need to write a validator for the h:inputText which performs some logic only if the value for that input is changed. I.e.
public class MyValidator implements Validator{
public void validate(FacesContext context,
UIComponent component,
Object value) throws ValidatorException;
if(valueChanged(UIComponent component)){ //The method checks if the value's changed
//do some piece of logic
}
return;
}
}
I dug into the queuing events of the UIInput and found this:
validateValue(context, newValue);
// If our value is valid, store the new value, erase the
// "submitted" value, and emit a ValueChangeEvent if appropriate
if (isValid()) {
Object previous = getValue();
setValue(newValue);
setSubmittedValue(null);
if (compareValues(previous, newValue)) {
queueEvent(new ValueChangeEvent(this, previous, newValue));
}
}
This piece of code is from the method, executed by the Validation phase callback. The first thought that popped into my head was queriyng all events fired during handling the request. The method queueEvent(FacesEvent) is implemented as follows:
public void queueEvent(FacesEvent event) {
if (event == null) {
throw new NullPointerException();
}
UIComponent parent = getParent();
if (parent == null) {
throw new IllegalStateException();
} else {
parent.queueEvent(event);
}
}
Therefore every such invokation will end up in UIViewRoot.queueEvent(FacesEvent) which is implemented as:
public void queueEvent(FacesEvent event) {
if (event == null) {
throw new NullPointerException();
}
// We are a UIViewRoot, so no need to check for the ISE
if (events == null) {
int len = PhaseId.VALUES.size();
List<List<FacesEvent>> events = new ArrayList<List<FacesEvent>>(len);
for (int i = 0; i < len; i++) {
events.add(new ArrayList<FacesEvent>(5));
}
this.events = events;
}
events.get(event.getPhaseId().getOrdinal()).add(event);
}
Which means, all events is actually stored as a List<List<FacesEvent>> for each phase. But the List<List<FacesEvent>> events is a private field, so it's impossible to get direct acces to it.
Another thing is that the actual validation is being perfromed before the quingEvent, so implemting valueChangeListener doesn't seem useful as well.
Question: Is it possible to implements such validator in JSF in a natural way?
Just do the value comparison yourself. In the validator, the old value is just readily available via UIComponent argument.
#Override
public void validate(FacesContext context, UIComponent component, Object submittedValue) {
if (component instanceof EditableValueHolder) {
Object newValue = submittedValue;
Object oldValue = ((EditableValueHolder) component).getValue();
if (newValue == null ? oldValue == null : newValue.equals(oldValue)) {
return; // Not changed, so skip validation.
}
}
// Do actual validation here.
}
If you happen to use JSF utility library OmniFaces, it has a ValueChangeValidator which does exactly this.

ActionContext.getContext().getParameters() returns null during StrutsJUnit4TestCase

I am running a JUnit test via maven where a struts action java method is being tested that makes the following call:
// Gets this from the "org.apache.struts2.util.TokenHelper" class in the struts2-core jar
String token = TokenHelper.getTokenName();
Here is the method in "TokenHelper.java":
/**
* Gets the token name from the Parameters in the ServletActionContext
*
* #return the token name found in the params, or null if it could not be found
*/
public static String getTokenName() {
Map params = ActionContext.getContext().getParameters();
if (!params.containsKey(TOKEN_NAME_FIELD)) {
LOG.warn("Could not find token name in params.");
return null;
}
String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD);
String tokenName;
if ((tokenNames == null) || (tokenNames.length < 1)) {
LOG.warn("Got a null or empty token name.");
return null;
}
tokenName = tokenNames[0];
return tokenName;
}
The 1st line in this method is returning null:
Map params = ActionContext.getContext().getParameters();
The next LOC down, "params.containKey(...)" throws a NullPointerException because "params" is null.
When this action is called normally, this runs fine. However, during the JUnit test, this Null Pointer occurs.
My test class looks like this:
#Anonymous
public class MNManageLocationActionTest extends StrutsJUnit4TestCase {
private static MNManageLocationAction action;
#BeforeClass
public static void init() {
action = new MNManageLocationAction();
}
#Test
public void testGetActionMapping() {
ActionMapping mapping = getActionMapping("/companylocation/FetchCountyListByZip.action");
assertNotNull(mapping);
}
#Test
public void testLoadStateList() throws JSONException {
request.setParameter("Ryan", "Ryan");
String result = action.loadStateList();
assertEquals("Verify that the loadStateList() function completes without Exceptions.",
result, "success");
}
}
The ActionContext.getContext() is at least no longer null after I switched to using StrutsJUnit4TestCase.
Any idea why .getParameters() is returning null?
You need to initialize parameters map by yourself inside your test method. Additionally if you want to get token name you need to put it in parameters map.
Map<String, Object> params = new HashMap<String, Object>();
params.put(TokenHelper.TOKEN_NAME_FIELD,
new String[] { TokenHelper.DEFAULT_TOKEN_NAME });
ActionContext.getContext().setParameters(params);

Programmatically created form and validation, unhandled faces messages

I create a form by using Apache MyFaces library. The form is related to the
jsf-page by a binding. Moreover I built a TestValidator-class which implements
Validator-interface. My form got shown, I enter my input, validation gots
triggered successfully. Unfortunately I am not able to display corresponding
FacesMessage. I guess I am not able to tell JSF 'Please rerender h:messages
after throwing the validator-Exception'.
What went wrong? Thanks in advance.
#FacesValidator("TestValidator") // class TestValidator
public class TestValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesMessage fm = new FacesMessage();
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
fm.setSummary("TestValidator FEHLER");
fm.setDetail("TestValidator FEHLER");
throw new ValidatorException(fm);
}
}
}
<!-- JSF-Snippet -->
<t:div id="formContainer" binding="#{formsbuilder.form}" />
private Div createFacesMessagesDiv() { // FormsBuilder-Bean-Snippet for creating the container for the FacesMessage
facesMessagesDiv = new Div(); // CORRESPONDING BEAN PROPERTY
facesMessagesDiv.setId("facesMessagesContainer");
facesMessagesDiv.setStyle("color: blue; border: 3px solid green;");
HtmlOutputText introText = new HtmlOutputText();
introText.setId("facesMessagesIntroText");
introText.setValue("FacesMessages - Start: ");
facesMessagesDiv.getChildren().add(introText);
HtmlMessages fms = new HtmlMessages();
fms.setId("facesMessages");
facesMessagesDiv.getChildren().add(fms);
HtmlOutputText outroText = new HtmlOutputText();
outroText.setId("facesMessagesOutroText");
outroText.setValue("FacesMessages - Ende!");
facesMessagesDiv.getChildren().add(outroText);
return facesMessagesDiv;
}
private HtmlAjaxCommandLink createSaveFormButton() { // saveButton after hitting this button, facesMessages should show up;
HtmlAjaxCommandLink saveFormButton = HtmlRendering.createGeneralButton("saveForm" + currentSubForm.getIdAsString(), /* ID */
"#{resources.labels['formsgenerator_saveForm']}",
"#{formsbuilder.submitForm}",
null, /* action */
null, /* actionReturnType */
null, /* onComplete */
"", /* styleClassValueExpression */
"", /* imageValue */
"modifiedContainerDiv");
saveFormButton.setReRender(facesMessagesDiv); // CORRESPONDING BEAN PROPERTY
saveFormButton.setValueExpression("oncomplete", HtmlRendering.createValueExpression("afterGeneralSavingSubForm()");
return saveFormButton;
}
It does not look like you are setting the message correctly.
You forgot to add the FacesMessage to the FacesContext.
Try this:
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesMessage fm = new FacesMessage();
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
fm.setSummary("TestValidator FEHLER");
fm.setDetail("TestValidator FEHLER");
FacesContext.getCurrentInstance().addMessage(null, fm);
throw new ValidatorException(fm);
}
}
Here's a more concise way to write a FacesMessage:
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("TestValidator.validate: " + value);
if (((String) value).equals("2")){
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "TestValidator FEHLER", "TestValidator FEHLER"));
throw new ValidatorException(fm);
}
}

class not found exception while adding validator class

0 with eclipse and glassfish, in fact i have added a custom validator to validate emai address, and i am getting class not found exception. although i have registered in faces.config too.
here is my validator code
public static final String EmailPattern= "^[_A-Za-z0-9-]+(\\." +
"[_A-Za-z0-9-]+)*#[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*" +
"(\\.[A-Za-z]{2,})$";
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
matcher = pattern.matcher(value.toString());
if(!matcher.matches())
{
FacesMessage message = new FacesMessage("Please enter a valid email address");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
i registerd in faces.config
<validator>
<validator-id>emailvalidator</validator-id>
<validator-class>com.jsf.validators.EmailValidator</validator-class>
</validator>
does someone know what is the problem in it.

Resources