How to handle exceptions in a FacesConverter? - validation

When the date format is not correct (for example when I manually post 13,02,2018 instead of 13.02.2018 and also other incorrect dates such as 13.02.999) the app crashes. How can I fix it? (the manual input is important, i can`t just disable it).
XHTML:
<rich:calendar enableManualInput="true" datePattern="dd.MM.yyyy"
value="#{myBean.data.myDate}">
<f:converter converterId="mydate"/>
</rich:calendar>
Converter:
#FacesConverter("mydate")
public class LocalDateConverter implements Converter {
private static final DateTimeFormatter formatter;
static {
formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
formatter.withLocale(new Locale("ru"));
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return LocalDate.parse(value, formatter);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
} else if (value instanceof LocalDate) {
return ((LocalDate) value).format(formatter);
} else if (value instanceof LocalDateTime) {
return ((LocalDateTime) value).format(formatter);
} else {
throw new IllegalArgumentException("Value is not java.time.LocaleDate");
}
}

Converters should throw a ConverterException which can contain a FacesMessage. This message can be displayed on your XHTML page, near the input component that caused the exception using <h:message for="inputComponentId"/>.
The problem occurs in your getAsObject method. There you should catch the DateTimeParseException exception and throw a ConverterException:
try {
return LocalDate.parse(value, formatter);
}
catch (DateTimeParseException ex) {
throw new ConverterException(new FacesMessage("Invalid date: " + value));
}
See also:
https://docs.oracle.com/javaee/7/tutorial/jsf-custom010.htm
How to use java.time.ZonedDateTime / LocalDateTime in p:calendar

You don't need converter at all. Simply include label attribute in rich:calendar component and let system figure out if value is correct. Example:
<h:outputLabel for="programStartDate" value="#{msg.programStartDate}" />
<rich:calendar id="programStartDate" value="#{program.programStartDate}"
label="#{msg.programStartDate}" inputStyle="width: 100px;"
datePattern="#{referenceData.defaultDatePattern}"
timeZone="#{referenceData.timezone}"
enableManualInput="true" popup="true" required="true" />

use a try catch and catch the exception so it doesn't crash but continue without allowing the exception to crash your program
#FacesConverter("mydate")
public class LocalDateConverter implements Converter
{
private static final DateTimeFormatter formatter;
static {
formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
formatter.withLocale(new Locale("ru"));
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
return LocalDate.parse(value, formatter);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
try
{
if (value == null)
{
return "";
}
else if (value instanceof LocalDate) {
return ((LocalDate)value).format(formatter);
} else if (value instanceof LocalDateTime) {
return ((LocalDateTime)value).format(formatter);
} else {
throw new IllegalArgumentException("Value is not java.time.LocaleDate");
}
}
catch (Exception)
{
return "SOME DEFAULT DATE";
}
}
}

Related

SpringMVC Converter Exception Handling

I implemented a converter to convert String to UUID and include length validation.
public class StringToUUIDConverter implements Converter<String, UUID> {
public UUID convert(String source){
if(source.length>36){
throw new RuntimeException();
}else{
return ...
}
}
}
At first, I thought I would get a RuntimeException when I passed in a string of length 37. However, I tested it and found that no exception was thrown.
Code snippet of TypeConverterDelegate.convertIfNecessary()
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
ConversionUtils convert all exception to ConversionFailedException :
abstract class ConversionUtils {
#Nullable
public static Object invokeConverter(GenericConverter converter, #Nullable Object source,
TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
}
TypeConverterDelegate catches the exception I throw and follows another set of logic to convert String to UUID. Although I've done what I want with InitBinder, what should I do if I really want to throw an exception here?

Ajax not work on primefaces selectManyCheckbox with converter

I'm trying to use primefaces selectManyCheckBox with ajax and converter, but the ajax not fired. If I'm didn't use converter, the ajax can fired. Is there something wrong with my converter?
<div class="form-group">
<p:outputLabel value="Atur Grade Pinjaman" for="gradePinjaman"/>
<p:selectManyCheckbox id="gradePinjaman" value="#{autoInvestController.param.grades}" converter="companyGradeConverter">
<f:selectItems value="#{autoInvestController.grades}" var="grade" itemLabel="#{grade.id}" itemValue="#{grade}"/>
<p:ajax update="selectAll estimation" listener="#{autoInvestController.valueChange}"/>
</p:selectManyCheckbox>
<p:message for="gradePinjaman"/>
</div>
Here is my backing bean code
public void valueChange() {
if (param.getGrades() != null && !param.getGrades().isEmpty()) {
checkAll = param.getGrades().size() == grades.size();
calculateCollectEstimation();
}
}
Here is my converter
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (Strings.isNullOrEmpty(value)) {
return null;
} else {
try {
PlatformService platformService = (PlatformService) CDI.current().select(PlatformService.class).get();
Map<String, Object> param = new HashMap<>();
param.put("id", value);
CompanyGradeResponse companyGrade = platformService.getCompanyGrade(param).get(0);
return companyGrade;
} catch (EndpointException e) {
LOG.error(e.getMessage(), e);
return null;
}
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value != null) {
return ((CompanyGradeResponse) value).getId();
} else {
return null;
}
}
I think my converter work well, but why the ajax won't fired when i check the checkbox?
Thanks

JSF Validation using DAO doesn't work

I'm trying to validate the information, user is giving at the registration. One of the fields contains the mailadress, which should be validated by looking in the database to confirm, it doesn't exist yet.
Problem is, that if i type an existing mailadress, it will give back a NonUniqueResultException, but also does store the new user with the duplicate mailadress in the database. Don't understand this, beacuse in the JSF-lifecycle after validation fails, it shouldn't go on to the invoke application phase, right?
Here's my code:
mail field in register formular
<b:inputText id="mail" required="true"
requiredMessage="Bitte geben Sie Ihre E-Mail-Adresse an!"
label="E-Mail" placeholder="name#example.com" value="#{registrierenManagedBean.nutzer.mail}">
<f:validator validatorId="mailValidatorRegistrieren"/>
<b:messages for="mail"/>
</b:inputText>
my custom validator
#FacesValidator("mailValidatorRegistrieren")
public class MailValidatorRegistrieren implements Validator {
#EJB
private DAO dao;
private String mail;
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object o) throws ValidatorException {
mail = (String)o;
boolean matchesPattern = EMAIL_PATTERN.matcher(mail).find();
if(!matchesPattern)
{
throw new ValidatorException((new FacesMessage("Invalid mail")));
}
if(mail.isEmpty()) {
return;
} else if(validateNutzer(mail)){
throw new ValidatorException(new FacesMessage("mail alredy used"));
} else{
return;
}
}
private boolean validateNutzer(String mail) {
try {
Nutzer n = dao.findNutzerByMail(mail);
return n.getMail().equals(mail);
} catch (NullPointerException e) {
return false;
}
}
}
and the "findNutzerByMail"-method from my DAO
public Nutzer findNutzerByMail(String mail) {
try {
return em.createNamedQuery("findNutzerByMail", Nutzer.class)
.setParameter("mail", mail)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}

Spring BigDecimal input

I hava a POJO object with one BigDecimal field sum.
In controller I add this POJO object as form like this:
MyForm form = new MyForm();
model.addAttribute("command", form);
My jsp:
<form:input path="sum" size="27"/>
In controller i add initbinder:
binder.registerCustomEditor(BigDecimal.class, new SumEditor());
Part of my SumEditor class:
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(parseMoney(text));
}
private BigDecimal parseMoney(String str) {
try {
return new BigDecimal(str);
} catch (Exception e) {
logger.error("error", e);
}
return null;
}
But in JSP view I see (in input field): |null________|
How fix this? I need: |___________|
You should simply override getText method of SumEditor to have it return an empty string ("") for a null value :
#Override
public String getAsText() {
if (getValue == null) {
return "";
}
BigDecimal val = (BigDecimal) getValue();
return val.toStr(); // or whatever conversion you need
}

Custom View Scope with SetPropertyActionListener

I'm using Spring with a custom view scope to create view scope beans. This works fine until I tried to inject a property using setPropertyActionListener. This works fine if I change the bean to request scope.
This is my view scope impl:
public class ViewScope implements Scope {
public Object get(String name, ObjectFactory<?> objectFactory) {
if (FacesContext.getCurrentInstance().getViewRoot() != null) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
} else {
return null;
}
}
public Object remove(String name) {
if (FacesContext.getCurrentInstance().getViewRoot() != null) {
return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
} else {
return null;
}
}
public void registerDestructionCallback(String name, Runnable callback) {
// Do nothing
}
public Object resolveContextualObject(String key) {
return null;
}
public String getConversationId() {
return null;
}
}
and my JSF:
<h:commandLink action="confirm.xhtml">
<f:setPropertyActionListener target="#{quoteHolder.item}" value="#{quote}"/>
</h:commandLink>

Resources