Skip to main content

A Case Study of Custom JSF Converters - Automatically Converting Years With 2 Digits Into 4 Digits


You can find the demonstrated code of this post on my Github repo here.

The user story

" As a banker, I want to enter a client's birthday like 01.01.80 or 01.01.1980,
So that the birthday can be displayed as 01.01.1980 "

Implementation

Firstly, I thought about how to use a built-in converter likes the following.

<h:inputText id="birthdate" 
 value="#{data.birthdate}" 
 type="date" >
 <f:convertDateTime/> 
 <f:ajax event="change" 
   listener="#{data.onCalculate}" 
   execute="@this"
   render="@this" />
</h:inputText>

However, without defining a pattern, JSF used its default one which was not my desire. It threw an exception when I tried to enter a date like "01.01.90".

> myform:birthdate: '01.01.90' could not be understood as a date. Example: Mar 4, 2018 

Actually, I even could not define either pattern "dd.MM.yyyy" or "dd.MM.yy" for "f:convertDateTime":
- "dd.MM.yyyy" --> 01.01.80 becomes 01.01.0080
- "dd.MM.yy" --> 01.01.1980 becomes 01.01.80

Well, this was a case for us to define our own custom converter.
- XHTML code
<h:inputText id="birthdate" 
 value="#{data.birthdate}" 
 type="date">
 <f:converter converterId="myDateTimeConverter" /> 
 <f:ajax event="change" 
   listener="#{data.onCalculate}" 
   execute="@this"
   render="@this" />
</h:inputText>

- Java code
package vn.nvanhuong.javalab.jsf.dateformat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;

import org.apache.commons.lang3.StringUtils;

@FacesConverter("myDateTimeConverter")
public class DateTimeConverter implements Converter {

 private static final String DD_MM_YY = "dd.MM.yy";
 private static final String DD_MM_YYYY = "dd.MM.yyyy";
 
 public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
  if (StringUtils.isEmpty(value)) {
   return null;
  }

  SimpleDateFormat formatter = new SimpleDateFormat(DD_MM_YY);
  Date parsedObject = null;
  try {
   parsedObject = formatter.parse(value);
  } catch (ParseException e) {
   throw new ConverterException(e);
  }
  return parsedObject;
 }
 
 public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
  if (value == null) {
   return null;
  }

  SimpleDateFormat formatter = new SimpleDateFormat(DD_MM_YYYY);
  return formatter.format(value);
 }

}

The idea was that I allowed users to enter the year with 4 or 2 digits. Then, I used SimpleDateFormatter to convert the entered value into a value with type Date at JSF's phase "Apply Request" which calls method "getAsObject".

The cool thing here was with pattern "dd.MM.yy" the formatter allowed us to do that! Then, we always displayed the date with format "dd.MM.yyyy" at JSF's phase "Render Response" which calls method "getAsString".

Comments

Popular posts from this blog

Strategy Design Pattern

For example, I have a program with an Animal abstract class and two sub-classes Dog and Bird. I want to add new behavior for the class Animal, this is "fly".  Now, I face two approaches to solve this issue: 1. Adding an abstract method "fly" into the class Animal. Then, I force the sub-classes should be implemented this method, something like: public abstract class Animal{ //bla bla public abstract void fly(); } public class Bird extends Animal{ //bla bla public void fly(){ System.out.println("Fly high"); } } public class Dog extends Animal{ //bla bla public void fly(){ System.out.println("Cant fly"); } } 2. Creating an interface with method "fly" inside. The same issue to an abstract class, I force the classes these implement this interface should have a method "fly" inside: public interface Flyable{ public void fly(); } public class Bird implements Flyable{ //bla bla public void fly(){ System.out.pr...

Junit - Test fails on French or German string assertion

In my previous post about building a regex to check a text without special characters but allow German and French . I met a problem that the unit test works fine on my machine using Eclipse, but it was fail when running on Jenkins' build job. Here is my test: @Test public void shouldAllowFrenchAndGermanCharacters(){ String source = "ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ"; assertFalse(SpecialCharactersUtils.isExistSpecialCharater(source)); } Production code: public static boolean isExistNotAllowedCharacters(String source){ Pattern regex = Pattern.compile("^[a-zA-Z_0-9_ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ]*$"); Matcher matcher = regex.matcher(source); return !matcher.matches(); } The result likes the following: Failed tests: SpecialCharactersUtilsTest.shouldAllowFrenchAndGermanCharacters:32 null A guy from stackoverflow.com says: "This is probably due to the default encoding used for your Java source files. The ö in the string literal in the J...

What the heck is Meteor DDP?

I was using Meteor for my messenger project. I was so curious about the real time connection. I wanted to know how exactly this mechanism works. In this post, I will go through the DDP Specification, an overview of WebSocket, and a simple demo about how to subscribe a publication of Rocket.Chat (containing a DDP server) from an external webpage. At a glance, I knew that Meteor invented a protocol called DDP which uses for handling real time connection. So then, what is DDP? "DDP (Distributed Data Protocol) is the stateful WebSocket protocol that Meteor uses to communicate between the client and the server." [1] All right! Why does DDP matter? "DDP is a standard way to solve the biggest problem facing client-side JavaScript developers: querying a server-side database, sending the results down to the client, and then pushing changes to the client whenever anything changes in the database" . [2] In order to understand deeply the protocol, I decided ...

Multiple Inheritance of State and Implementation

Today, I was just curious about why an enum can not extend anything else. I took a look on the Oracle document here , and I found the answer is below: "All enums implicitly extend java.lang.Enum. Because a class can only extend one parent (see Declaring Classes), the Java language does not support multiple inheritance of state (see Multiple Inheritance of State, Implementation, and Type), and therefore an enum cannot extend anything else." I have been learned of it before. But, wait a sec...! Why Java does not support multiple inheritance of state? Since I have worked with other programming languages like C++, I was able to make a class extend some other classes. The short answer is to avoid the issues of multiple inheritance of state .  I wonder if other programming languages have these below terms but Java does. Multiple inheritance of state It is the ability to inherit fields from multiple classes. There is a problem and Java avoids it. "For exa...

Why Functional Programming Matter

What issues do we concern when implementing and maintaining systems? One of the most concern is debugging during maintenance: "this code crashed because it observed some unexpected value." Then, it turns out that the ideas of  no side effects  and  immutability , which functional programming promotes, can help. Shared mutable data is the root cause Shared mutable data are read and updated by more than one of the methods. Share mutable data structures make it harder to track changes in different parts of your program. An immutable object is an object that can't change its state after it's instantiated so it can't be affected by the actions of a function. It would be a dream to maintain because we wouldn't have any bad surprises about some object somewhere that unexpectedly modifies a data structure. A new thinking: Declarative programming There are two ways thinking about implementing a system by writing a program. - Imperative programming: has...