CustomRequiredvalidator did not get triggered - validation

I am confused because my custom required validation did not get triggered.
I got a class which creates my HtmlTextInput element programmatically and adds
the validator. Moreover I got my custom validator class. Console tells me that
validators got bound to HtmlTextInput. Anyway after hitting 'save'
CustomRequiredValidator did not get called! I am using JSF 2.x. Thanks in
advance.
// programmatically built HtmlInput-element
if (freeText.isRequired()) {
// Validator-Objekt
System.out.println("CustomRequiredValidator bound.");
final CustomRequiredValidator validator = (CustomRequiredValidator) FacesContext.getCurrentInstance().getApplication().createValidator("CustomRequiredValidator");
inputText.addValidator(validator);
}
// CustomValidator
#FacesValidator("CustomRequiredValidator")
public class CustomRequiredValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object value) throws ValidatorException {
System.out.println("RequiredValidator.validate ..."); ...
}
}

Validators are only invoked if conversion has succeed. So if there's a converter, either explicitly registered or implicitly used (e.g. having an Integer bean property would trigger the JSF builtin IntegerConverter) and it threw a ConverterException, then the validator will never be triggered. You should however have noticed this converter exception in any of the associated <h:message(s)> component or the server logs.
If the converter is excluded from being suspect, then another possible cause is that JSF is configured to not validate empty fields by having the following context parameter in webapp's web.xml:
<context-param>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>false</param-value>
</context-param>
Other than that, well, running a debugger, starting with a breakpoint at UIInput#validate() method should give insights.

Related

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.

Show error details when throwing Validator Exception

I have code in a managed bean:
public void setTestProp(String newProp) {
FacesMessage yourFailure = new FacesMessage();
yourFailure.setDetail("Really you need to promise to never do that again!");
yourFailure.setSummary("Stop here, now!");
yourFailure.setSeverity(FacesMessage.SEVERITY_FATAL);
throw new ValidatorException(yourFailure);
}
and in the XPage:
<xp:messages id="messages1" layout="table" showSummary="false"
showDetail="true" globalOnly="false">
</xp:messages>
but I get as result message (nicely in the yellow box as expected, not in an error page):
Error setting property 'testProp' in bean of type com.ibm.sg.demo.Test: javax.faces.validator.ValidatorException: Stop here, now!
I would like to:
not have the technical part
see the summary
What do I miss?
The problem is that the property resolver catches all java.lang.Throwable from the get/set method of the managed beans. The "original" facesMessage is replaced with a new one (the previous message is appended).
You have three possibilities:
Create your own property resolver
Create your own validator and attach it to the field binded to the managed bean
Add a validation method to your bean
Hope this helps
Sven
EDIT:
How to add a validation method to your bean
a) Add a validation method to your bean
public void validate(FacesContext context, UIComponent toValidate, Object value){
// Do your validation with value
// if everything is ok, exit method
// if not, flag component invalid...
((UIInput)toValidate).setValid(false);
// ... create your message ...
FacesMessage yourFailure = new FacesMessage();
yourFailure.setDetail("Really you need to promise to never do that again!");
yourFailure.setSummary("Stop here, now!");
yourFailure.setSeverity(FacesMessage.SEVERITY_FATAL);
context.addMessage(toValidate.getClientId(context), yourFailure);
}
b) Add your validator to the field
<xp:inputText id="inputText1"
value="#{TBean.test}"
validator="#{TBean.validate}">
(You can name the method whatever you want.)
This validator has not to be added to the faces-config.xml.
Once you determine your field has failed validation, all you need to do is do this, and you'll get what you want:
throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Stop here, now!"));

Why does <h:inputText required="true"> allow blank spaces?

When I set required="true" in a <h:inputText>, it still allows blank spaces. I have been trying to modify the jsf-api.jar but I could not understand how to generate new a JAR, so I tried to modify the isEmpty() method from UIInput class and compile it, open the jsf-api.jar and replace it with the new one, but it did not work.
What I need is to do trim() when the user writes in a <h:inputText> to do not allow blank spaces. How can I achieve this?
If you want to download the jsf-api.jar resource, you can do it, just read how to at: http://javaserverfaces.java.net/checkout.html.
That's normal and natural behaviour and not JSF specific. A blank space may be perfectly valid input. The required="true" only kicks in on empty inputs, not in filled inputs. In JSF you can however just create a Converter for String class to automatically trim the whitespace.
#FacesConverter(forClass=String.class)
public class StringTrimmer implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value != null ? value.trim() : null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (String) value;
}
}
Put this class somewhere in your project. It'll be registered automatically thanks to #FacesConverter and invoked automatically for every String entry thanks to forClass=String.class.
No need to hack the JSF API/impl. This makes no sense.
If you want to turn off the behavior that BalusC notes as one of the answers as standard JSF behavior, you can modify the web.xml and include the following.
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
<context-param>
This will trigger the JSF framework to consider the values null which may be preferable, or an alternative to the answer from BalusC.

Why is my Spring 3 Validator Validating Everything on the Model?

I have a spring 3 controller with a validator for one of the methods. It insists on validating every object on the model. Would anyone be able to explain to me why it does this or if I'm doing something wrong?
According to the docs, 5.7.4.3 Configuring a JSR-303 Validator for use by Spring MVC (http://static.springsource.org/spring/docs/3.0.0.RC3/spring-framework-reference/html/ch05s07.html)
With JSR-303, a single javax.validation.Validator instance typically validates all model objects that declare validation constraints. To configure a JSR-303-backed Validator with Spring MVC, simply add a JSR-303 Provider, such as Hibernate Validator, to your classpath. Spring MVC will detect it and automatically enable JSR-303 support across all Controllers.
Example:
#Controller
public class WhaleController {
#Autowired
private Validator myValidator;
#Autowired
private WhaleService whaleService;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(this.myValidator);
}
#RequestMapping(value="/save-the-whales")
#Transactional
public void saveTheWhales(#Valid WhaleFormData formData, BindingResult errors, Model model) {
if (!errors.hasFieldErrors()) {
Whale whale = new Whale();
whale.setBreed( formData.getBreed() );
this.whaleService.saveWhale( whale );
model.addAttribute("whale", whale);
}
model.addAttribute("errors", errors.getFieldErrors());
}
}
When run it will complain that Whale is an invalid target for myValidator (which is set to validate WhaleFormData, and does so fine). Whale is a POJO with no validation constraints, annotation and no config anywhere. Through trial and error I've found that ANY object placed on the model will attempt to be validated and fail if the validator is not setup to handle it. Primitives are just fine.
Can anyone tell me why this is, point me to the appropriate documentation and/or tell me the best way to put something on the model without having it validated?
In the case above I would like to place "whale" on the model as it will now have a unique whaleId() that it received from my persistence layer.
Thanks!
I guess this behaviour is not covered in the documentation well.
The problem is caused by the following:
By default, #InitBinder-annotated method is called for each non-primitive model attribute, both incoming and outcoming (the purpose of calling it for outcoming attibutes is to allow you to register custom PropertyEditors, which are used by form tags when rendering a form).
DataBinder.setValidator() contains a defensive check that call Validator.supports() and throws an exception if false is returned. So, there is no attempt to perform a validation, just an early check.
The solution is to restrict the scope of #InitBinder to particular attribute:
#InitBinder("whaleFormData")
protected void initBinder(WebDataBinder binder) { ... }

i18n translation in JSP custom tag

Is it possible to write a custom JSP tag to take an i18n message key and output the translation phrase for the given request?
Normally in JSP/JSTL, I do:
<fmt:message key="${messageKey}"><fmt:param>arg1</fmt:param></fmt:message>
And I get the translation phrase. Now I need to do the following (there's a good reason for this):
<custom:translate key="${messageKey}" arg="arg1"/>
But I don't know how to look up the translation in the custom tag code. The TagSupport base class provides a pageContext from which I can get a ServletRequest which has the Locale... but how do I then look up the translation for a key?
I use Spring 3.0 and in my application-context.xml, I've defined a ReloadableBundleMessageSource so I can call:
messageSource.getMessage(
key, new Object[] {arg}, pageContext.getRequest().getLocale()
);
but I don't think I can inject messageSource into a custom tag, can I? Otherwise I can instantiate a new one, but would it load my tens of thousands of translations for every call? I don't want to resort to making messageSource a static member of a static class.
I don't do Spring, but in "plain" JSP you can just put the ResourceBundle instance in the session scope with help of a Filter or Servlet
ResourceBundle bundle = ResourceBundle.getBundle(basename, request.getLocale());
request.getSession().setAttribute("bundle", bundle);
And treat it in JSP like any other bean in EL.
${bundle[messageKey]}
It must be possible to have Spring to put that as a bean in the session scope.
There is a utility in spring to access the web application context. Then you can look up a bean by its name or type.
To get hold of your resource bundle, you could do something like:
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
messageResource = springContext.getBean("messageResource");
This question is very old but I think it is worth to share another way to solve this.
To access the Spring message source in a custom tag you only need to extend the class org.springframework.web.servlet.tags.RequestContextAwareTag instead of TagSupport.
In this case you have to implement the method doStartTagInternal() instead of doStartTag(), but inside this method you will have access to the MessageSource through getRequestContext().getMessageSource() method.
Therefore, your class would look like:
public class CreateCustomFieldTag extends RequestContextAwareTag{
//variables (key, arg...), getters and setters
#Override
protected int doStartTagInternal() throws Exception {
getRequestContext().getMessageSource().getMessage(
key, new Object[] {arg}, getRequestContext().getLocale());
}
}

Resources